/*-------------------------------------------------------------------------
 *
 * deparse.c
 *		  postgres_fdw 的查询去解析器
 *
 * 本文件包含检查查询 WHERE 子句以验证
 * 是否可以安全发送到远程服务器执行的函数，以及构造
 * 要发送的查询文本的函数。后者的
 * 功能在规则工具中是令人烦恼的重复，但有足够的特殊考虑，使得将其保持独立似乎是最好的选择。
 * 仅有的一点好处是，我们只需要对我们认为安全发送的节点类型进行去解析逻辑。
 *
 * 我们假设远程会话的搜索路径恰好是“pg_catalog”，
 * 因此我们需要为除 pg_catalog 之外的所有名称添加模式限定。
 *
 * 我们不认为发送 COLLATE 表达式到
 * 远程服务器是安全的：它可能没有与我们相同的排序名称。
 * （以后我们可能会考虑发送 COLLATE "C" 是安全的，但即使如此，在旧的远程服务器上
 * 也会失败。） 一个表达式被认为可以安全发送
 * 仅在于它中使用的所有运算符/函数输入排序是可以追溯到
 * 外部表的 Var(s)。这意味着如果远程服务器得到的答案与我们不同，
 * 外部表的列没有使用与远程表的列匹配的排序，这我们可以
 * 视为用户错误。
 *
 * Portions Copyright (c) 2012-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		  contrib/postgres_fdw/deparse.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/optimizer.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "postgres_fdw.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
#include "commands/tablecmds.h"

/*
 * 外部表达式遍历器搜索表达式树的全局上下文。
 */
typedef struct foreign_glob_cxt
{
	PlannerInfo *root;			/* 全局规划状态 */
	RelOptInfo *foreignrel;		/* 我们正在规划的外部关系 */
	Relids		relids;			/* 基础关系的 relids 在底层
								 * 扫描中 */
} foreign_glob_cxt;

/*
 * 外部表达式遍历器搜索的局部（逐树级别）上下文。
 * 这与识别表达式中使用的排序规则有关。
 */
typedef enum
{
	FDW_COLLATE_NONE,			/* 表达式属于不可排序的类型，或
								 * 它具有无法追溯到外部 Var 的默认排序规则 */
	FDW_COLLATE_SAFE,			/* 排序规则源自外部 Var */
	FDW_COLLATE_UNSAFE			/* 排序规则是非默认的，并且源自
								 * 除外部 Var 之外的其他来源 */
} FDWCollateState;

typedef struct foreign_loc_cxt
{
	Oid			collation;		/* 当前排序规则的 OID（如果有） */
	FDWCollateState state;		/* 当前排序规则选择的状态 */
} foreign_loc_cxt;

/*
 * deparseExpr 的上下文
 */
typedef struct deparse_expr_cxt
{
	PlannerInfo *root;			/* 全局规划状态 */
	RelOptInfo *foreignrel;		/* 我们正在规划的外部关系 */
	RelOptInfo *scanrel;		/* 底层扫描关系。与
								 * foreignrel 相同，当其表示一个联接或
								 * 基础关系时。 */
	StringInfo	buf;			/* 用于附加的输出缓冲区 */
	List	  **params_list;	/* 将成为远程参数的 exprs */
} deparse_expr_cxt;

#define REL_ALIAS_PREFIX	"r"
/* 方便的宏，用于添加关系名称限定 */
#define ADD_REL_QUALIFIER(buf, varno)	\
		appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))
#define SUBQUERY_REL_ALIAS_PREFIX	"s"
#define SUBQUERY_COL_ALIAS_PREFIX	"c"

/*
 * 检查一个表达式是否可以安全地在
 * 远程服务器上评估的函数。
 */
static bool fc_foreign_expr_walker(Node *fc_node,
								foreign_glob_cxt *fc_glob_cxt,
								foreign_loc_cxt *fc_outer_cxt,
								foreign_loc_cxt *fc_case_arg_cxt);
static char *fc_deparse_type_name(Oid fc_type_oid, int32 fc_typemod);

/*
 * 构建节点树的字符串表示的函数。
 */
static void fc_deparseTargetList(StringInfo fc_buf,
							  RangeTblEntry *fc_rte,
							  Index fc_rtindex,
							  Relation fc_rel,
							  bool fc_is_returning,
							  Bitmapset *fc_attrs_used,
							  bool fc_qualify_col,
							  List **fc_retrieved_attrs);
static void fc_deparseExplicitTargetList(List *fc_tlist,
									  bool fc_is_returning,
									  List **fc_retrieved_attrs,
									  deparse_expr_cxt *fc_context);
static void fc_deparseSubqueryTargetList(deparse_expr_cxt *fc_context);
static void fc_deparseReturningList(StringInfo fc_buf, RangeTblEntry *fc_rte,
								 Index fc_rtindex, Relation fc_rel,
								 bool fc_trig_after_row,
								 List *fc_withCheckOptionList,
								 List *fc_returningList,
								 List **fc_retrieved_attrs);
static void fc_deparseColumnRef(StringInfo fc_buf, int fc_varno, int fc_varattno,
							 RangeTblEntry *fc_rte, bool fc_qualify_col);
static void fc_deparseRelation(StringInfo fc_buf, Relation fc_rel);
static void fc_deparseExpr(Expr *fc_expr, deparse_expr_cxt *fc_context);
static void fc_deparseVar(Var *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseConst(Const *fc_node, deparse_expr_cxt *fc_context, int fc_showtype);
static void fc_deparseParam(Param *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseSubscriptingRef(SubscriptingRef *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseFuncExpr(FuncExpr *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseOpExpr(OpExpr *fc_node, deparse_expr_cxt *fc_context);
static bool fc_isPlainForeignVar(Expr *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseOperatorName(StringInfo fc_buf, Form_pg_operator fc_opform);
static void fc_deparseDistinctExpr(DistinctExpr *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseScalarArrayOpExpr(ScalarArrayOpExpr *fc_node,
									 deparse_expr_cxt *fc_context);
static void fc_deparseRelabelType(RelabelType *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseBoolExpr(BoolExpr *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseNullTest(NullTest *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseCaseExpr(CaseExpr *fc_node, deparse_expr_cxt *fc_context);
static void fc_deparseArrayExpr(ArrayExpr *fc_node, deparse_expr_cxt *fc_context);
static void fc_printRemoteParam(int fc_paramindex, Oid fc_paramtype, int32 fc_paramtypmod,
							 deparse_expr_cxt *fc_context);
static void fc_printRemotePlaceholder(Oid fc_paramtype, int32 fc_paramtypmod,
								   deparse_expr_cxt *fc_context);
static void fc_deparseSelectSql(List *fc_tlist, bool fc_is_subquery, List **fc_retrieved_attrs,
							 deparse_expr_cxt *fc_context);
static void fc_deparseLockingClause(deparse_expr_cxt *fc_context);
static void fc_appendOrderByClause(List *fc_pathkeys, bool fc_has_final_sort,
								deparse_expr_cxt *fc_context);
static void fc_appendLimitClause(deparse_expr_cxt *fc_context);
static void fc_appendConditions(List *fc_exprs, deparse_expr_cxt *fc_context);
static void fc_deparseFromExprForRel(StringInfo fc_buf, PlannerInfo *fc_root,
								  RelOptInfo *fc_foreignrel, bool fc_use_alias,
								  Index fc_ignore_rel, List **fc_ignore_conds,
								  List **fc_params_list);
static void fc_deparseFromExpr(List *fc_quals, deparse_expr_cxt *fc_context);
static void fc_deparseRangeTblRef(StringInfo fc_buf, PlannerInfo *fc_root,
							   RelOptInfo *fc_foreignrel, bool fc_make_subquery,
							   Index fc_ignore_rel, List **fc_ignore_conds, List **fc_params_list);
static void fc_deparseAggref(Aggref *fc_node, deparse_expr_cxt *fc_context);
static void fc_appendGroupByClause(List *fc_tlist, deparse_expr_cxt *fc_context);
static void fc_appendOrderBySuffix(Oid fc_sortop, Oid fc_sortcoltype, bool fc_nulls_first,
								deparse_expr_cxt *fc_context);
static void fc_appendAggOrderBy(List *fc_orderList, List *fc_targetList,
							 deparse_expr_cxt *fc_context);
static void fc_appendFunctionName(Oid fc_funcid, deparse_expr_cxt *fc_context);
static Node *fc_deparseSortGroupClause(Index fc_ref, List *fc_tlist, bool fc_force_colno,
									deparse_expr_cxt *fc_context);

/*
 * 辅助函数
 */
static bool fc_is_subquery_var(Var *fc_node, RelOptInfo *fc_foreignrel,
							int *fc_relno, int *fc_colno);
static void fc_get_relation_column_alias_ids(Var *fc_node, RelOptInfo *fc_foreignrel,
										  int *fc_relno, int *fc_colno);


/*
 * 检查输入条件中的每个条件子句，并将它们分类为两组，
 * 返回为两个列表：
 * 	- remote_conds 包含可以远程评估的表达式
 * 	- local_conds 包含无法远程评估的表达式
 */
void classifyConditions(PlannerInfo *fc_root,
				   RelOptInfo *fc_baserel,
				   List *fc_input_conds,
				   List **fc_remote_conds,
				   List **fc_local_conds)
{
	ListCell   *fc_lc;

	*fc_remote_conds = NIL;
	*fc_local_conds = NIL;

	foreach(fc_lc, fc_input_conds)
	{
		RestrictInfo *fc_ri = lfirst_node(RestrictInfo, fc_lc);

		if (is_foreign_expr(fc_root, fc_baserel, fc_ri->clause))
			*fc_remote_conds = lappend(*fc_remote_conds, fc_ri);
		else
			*fc_local_conds = lappend(*fc_local_conds, fc_ri);
	}
}

/*
 * 如果给定的表达式可以安全地在外部服务器上评估，则返回 true。
 */
bool is_foreign_expr(PlannerInfo *fc_root,
				RelOptInfo *fc_baserel,
				Expr *fc_expr)
{
	foreign_glob_cxt fc_glob_cxt;
	foreign_loc_cxt fc_loc_cxt;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) (fc_baserel->fdw_private);

	/*
	 * 检查表达式是否由可以安全远程执行的节点组成。
	 */
	fc_glob_cxt.root = fc_root;
	fc_glob_cxt.foreignrel = fc_baserel;

	/*
	 * 对于上层关系，使用其下面的扫描关系中的 relids，
	 * 因为上层关系的 relids 当前并未被核心代码设置为任何
	 * 有意义的内容。对于其他关系，使用它们自己的 relids。
	 */
	if (IS_UPPER_REL(fc_baserel))
		fc_glob_cxt.relids = fc_fpinfo->outerrel->relids;
	else
		fc_glob_cxt.relids = fc_baserel->relids;
	fc_loc_cxt.collation = InvalidOid;
	fc_loc_cxt.state = FDW_COLLATE_NONE;
	if (!fc_foreign_expr_walker((Node *) fc_expr, &fc_glob_cxt, &fc_loc_cxt, NULL))
		return false;

	/*
	 * 如果表达式具有有效的排序规则且不源自外部变量，则该表达式将无法发送。
	 */
	if (fc_loc_cxt.state == FDW_COLLATE_UNSAFE)
		return false;

	/*
	 * 包含任何可变函数的表达式无法发送，
	 * 因为其结果不稳定。例如，发送 now() 到远程
	 * 可能会因时钟偏移而引起混淆。未来版本可能
	 * 能够以更细粒度做出这个选择。（我们最后检查这个
	 * 因为它需要大量昂贵的目录查找。）
	 */
	if (contain_mutable_functions((Node *) fc_expr))
		return false;

	/* 可以在远程服务器上评估 */
	return true;
}

/*
 * 检查表达式是否安全可以远程执行，如果安全则返回true。
 *
 * 此外，*outer_cxt会更新为排序信息。
 *
 * 如果该子表达式不在带参数的CASE内，则case_arg_cxt为NULL。
 * 否则，它指向从参数表达式派生的排序信息，
 * 任何CaseTestExpr都必须参考此信息。
 *
 * 我们必须检查表达式仅包含可以反解析的节点类型，
 * 所有类型/函数/操作符是否安全发送（它们是“可运输的”），
 * 并且表达式中使用的所有排序都源自外部表的Vars。
 * 由于后者，逻辑与parse_collate.c中的assign_collations_walker()非常相似，
 * 尽管我们在这里可以假设给定的表达式是有效的。注意函数的可变性在这里不被考虑。
 */
static bool fc_foreign_expr_walker(Node *fc_node,
					foreign_glob_cxt *fc_glob_cxt,
					foreign_loc_cxt *fc_outer_cxt,
					foreign_loc_cxt *fc_case_arg_cxt)
{
	bool		fc_check_type = true;
	PgFdwRelationInfo *fc_fpinfo;
	foreign_loc_cxt fc_inner_cxt;
	Oid			fc_collation;
	FDWCollateState fc_state;

	/* 对于空子表达式无需进行任何操作 */
	if (fc_node == NULL)
		return true;

	/* 可能需要来自baserel的fdw_private结构的服务器信息 */
	fc_fpinfo = (PgFdwRelationInfo *) (fc_glob_cxt->foreignrel->fdw_private);

	/* 为可能递归到子节点设置inner_cxt */
	fc_inner_cxt.collation = InvalidOid;
	fc_inner_cxt.state = FDW_COLLATE_NONE;

	switch (nodeTag(fc_node))
	{
		case T_Var:
			{
				Var		   *fc_var = (Var *) fc_node;

				/*
				 * 如果Var来自外部表，我们认为其
				 * 排序（如果有）是安全使用的。如果来自其他
				 * 表，我们将其排序处理得如同
				 * Param的排序，即不安全拥有一个
				 * 非默认排序。
				 */
				if (bms_is_member(fc_var->varno, fc_glob_cxt->relids) &&
					fc_var->varlevelsup == 0)
				{
					/* Var属于外部表 */

					/*
					 * 除ctid外的系统列不应发送到
					 * 远程，因为我们没有努力确保
					 * 本地和远程值匹配（tableoid尤其
					 * 几乎肯定不匹配）。
					 */
					if (fc_var->varattno < 0 &&
						fc_var->varattno != SelfItemPointerAttributeNumber)
						return false;

					/* 否则检查排序 */
					fc_collation = fc_var->varcollid;
					fc_state = OidIsValid(fc_collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
				}
				else
				{
					/* Var属于其他某个表 */
					fc_collation = fc_var->varcollid;
					if (fc_collation == InvalidOid ||
						fc_collation == DEFAULT_COLLATION_OID)
					{
						/*
						 * 它是不可排序的，或者可以与
						 * 可排序的外部Var安全结合，因此将状态设置为NONE。
						 */
						fc_state = FDW_COLLATE_NONE;
					}
					else
					{
						/*
						 * 不要立刻失败，因为Var可能出现在
						 * 不敏感于排序的上下文中。
						 */
						fc_state = FDW_COLLATE_UNSAFE;
					}
				}
			}
			break;
		case T_Const:
			{
				Const	   *fc_c = (Const *) fc_node;

				/*
				 * 如果常量有非默认排序，它可能是一个
				 * 非内置类型，或者反映了CollateExpr的折叠。
				 * 除非它在不敏感于排序的上下文中使用，
				 * 否则将其发送到远程是不安全的。
				 */
				fc_collation = fc_c->constcollid;
				if (fc_collation == InvalidOid ||
					fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_Param:
			{
				Param	   *fc_p = (Param *) fc_node;

				/*
				 * 如果它是一个MULTIEXPR Param，则直接返回。
				 * 我们无法从这里判断引用的子链接/子计划是否包含任何远程
				 * Vars；如果包含，处理起来太复杂，
				 * 目前不考虑支持。幸运的是，MULTIEXPR
				 * Params在规划结束之前不会被简化为普通的PARAM_EXEC，
				 * 因此我们可以很容易地检测到这种情况。 （正常的
				 * PARAM_EXEC Params是安全的，因为它们的值
				 * 来自计划树中其他地方；但MULTIEXPR
				 * 在同一目标列表中的其他地方引用了一个子选择，
				 * 因此如果我们想处理这样的直接外部更新，
				 * 我们就必须以某种方式评估它。）
				 */
				if (fc_p->paramkind == PARAM_MULTIEXPR)
					return false;

				/*
				 * 排序规则与Consts和非外部Vars相同。
				 */
				fc_collation = fc_p->paramcollid;
				if (fc_collation == InvalidOid ||
					fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_SubscriptingRef:
			{
				SubscriptingRef *fc_sr = (SubscriptingRef *) fc_node;

				/* Assignment should not be in restrictions. */
				if (fc_sr->refassgnexpr != NULL)
					return false;

				/*
				 * 递归进入剩余的子表达式。容器的下标不会影响
				 * SubscriptingRef 结果的排序，因此先处理这些，
				 * 然后重置 inner_cxt。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_sr->refupperindexpr,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;
				fc_inner_cxt.collation = InvalidOid;
				fc_inner_cxt.state = FDW_COLLATE_NONE;
				if (!fc_foreign_expr_walker((Node *) fc_sr->reflowerindexpr,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;
				fc_inner_cxt.collation = InvalidOid;
				fc_inner_cxt.state = FDW_COLLATE_NONE;
				if (!fc_foreign_expr_walker((Node *) fc_sr->refexpr,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 容器下标通常产生与 refexpr 相同的排序，但如果
				 * 不同，则使用与函数节点相同的逻辑。
				 */
				fc_collation = fc_sr->refcollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_FuncExpr:
			{
				FuncExpr   *fc_fe = (FuncExpr *) fc_node;

				/*
				 * 如果表达式使用的函数不可传输，则无法发送到
				 * 远程，因为它在远程端可能具有不兼容的语义。
				 */
				if (!is_shippable(fc_fe->funcid, ProcedureRelationId, fc_fpinfo))
					return false;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_fe->args,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 如果函数的输入排序不是源自外部 Var，
				 * 则无法发送到远程。
				 */
				if (fc_fe->inputcollid == InvalidOid)
					 /* OK, inputs are all noncollatable */ ;
				else if (fc_inner_cxt.state != FDW_COLLATE_SAFE ||
						 fc_fe->inputcollid != fc_inner_cxt.collation)
					return false;

				/*
				 * 检测节点是否引入了不源自外部 Var 的排序。
				 * （如果是这样，我们只是将其标记为不安全，而不是
				 * 立即返回 false，因为父节点可能不在乎。）
				 */
				fc_collation = fc_fe->funccollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_OpExpr:
		case T_DistinctExpr:	/* 相当于OpExpr的结构 */
			{
				OpExpr	   *fc_oe = (OpExpr *) fc_node;

				/*
				 * 同样，只有可传输的操作符才能发送到远程。
				 * （如果操作符是可传输的，我们假设其底层
				 * 函数也是如此。）
				 */
				if (!is_shippable(fc_oe->opno, OperatorRelationId, fc_fpinfo))
					return false;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_oe->args,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 如果操作符的输入排序不是源自外部 Var，
				 * 则无法发送到远程。
				 */
				if (fc_oe->inputcollid == InvalidOid)
					 /* OK, inputs are all noncollatable */ ;
				else if (fc_inner_cxt.state != FDW_COLLATE_SAFE ||
						 fc_oe->inputcollid != fc_inner_cxt.collation)
					return false;

				/* Result-collation handling is same as for functions */
				fc_collation = fc_oe->opcollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_ScalarArrayOpExpr:
			{
				ScalarArrayOpExpr *fc_oe = (ScalarArrayOpExpr *) fc_node;

				/*
				 * 再次，只有可传输的操作符才能发送到远程。
				 */
				if (!is_shippable(fc_oe->opno, OperatorRelationId, fc_fpinfo))
					return false;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_oe->args,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 如果操作符的输入排序不是源自外部 Var，
				 * 则无法发送到远程。
				 */
				if (fc_oe->inputcollid == InvalidOid)
					 /* OK, inputs are all noncollatable */ ;
				else if (fc_inner_cxt.state != FDW_COLLATE_SAFE ||
						 fc_oe->inputcollid != fc_inner_cxt.collation)
					return false;

				/* Output is always boolean and so noncollatable. */
				fc_collation = InvalidOid;
				fc_state = FDW_COLLATE_NONE;
			}
			break;
		case T_RelabelType:
			{
				RelabelType *fc_r = (RelabelType *) fc_node;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_r->arg,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * RelabelType 不能引入不源自输入外部 Var 的排序
				 * （与真实函数的逻辑相同）。
				 */
				fc_collation = fc_r->resultcollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_BoolExpr:
			{
				BoolExpr   *fc_b = (BoolExpr *) fc_node;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_b->args,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/* Output is always boolean and so noncollatable. */
				fc_collation = InvalidOid;
				fc_state = FDW_COLLATE_NONE;
			}
			break;
		case T_NullTest:
			{
				NullTest   *fc_nt = (NullTest *) fc_node;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_nt->arg,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/* Output is always boolean and so noncollatable. */
				fc_collation = InvalidOid;
				fc_state = FDW_COLLATE_NONE;
			}
			break;
		case T_CaseExpr:
			{
				CaseExpr   *fc_ce = (CaseExpr *) fc_node;
				foreign_loc_cxt fc_arg_cxt;
				foreign_loc_cxt fc_tmp_cxt;
				ListCell   *fc_lc;

				/*
				 * 递归到 CASE 的参数表达式（如果有的话）。
				 * 其排序必须在检查 WHEN 表达式中的 CaseTestExprs 时
				 * 保存以备使用。
				 */
				fc_arg_cxt.collation = InvalidOid;
				fc_arg_cxt.state = FDW_COLLATE_NONE;
				if (fc_ce->arg)
				{
					if (!fc_foreign_expr_walker((Node *) fc_ce->arg,
											 fc_glob_cxt, &fc_arg_cxt, fc_case_arg_cxt))
						return false;
				}

				/* Examine the CaseWhen subexpressions. */
				foreach(fc_lc, fc_ce->args)
				{
					CaseWhen   *fc_cw = lfirst_node(CaseWhen, fc_lc);

					if (fc_ce->arg)
					{
						/*
						 * 在带参数的CASE中，解析器应该生成
						 * 形式为"CaseTestExpr = RHS"的WHEN子句，
						 * 可能在CaseTestExpr上插入了隐式强制转换。
						 * 然而，在经过优化器处理的表达式中，WHEN子句可以
						 * 是几乎任何东西（因为等式运算符
						 * 可能已扩展为内联函数）。
						 * 在这种情况下禁止推送，因为
						 * deparseCaseExpr无法处理它。
						 */
						Node	   *fc_whenExpr = (Node *) fc_cw->expr;
						List	   *fc_opArgs;

						if (!IsA(fc_whenExpr, OpExpr))
							return false;

						fc_opArgs = ((OpExpr *) fc_whenExpr)->args;
						if (list_length(fc_opArgs) != 2 ||
							!IsA(strip_implicit_coercions(linitial(fc_opArgs)),
								 CaseTestExpr))
							return false;
					}

					/*
					 * 递归到WHEN表达式，传递参数信息。
					 * 它的排序规则不会影响结果（实际上，它
					 * 应该是布尔值，因此不应有排序规则）。
					 */
					fc_tmp_cxt.collation = InvalidOid;
					fc_tmp_cxt.state = FDW_COLLATE_NONE;
					if (!fc_foreign_expr_walker((Node *) fc_cw->expr,
											 fc_glob_cxt, &fc_tmp_cxt, &fc_arg_cxt))
						return false;

					/* 递归到THEN表达式。 */
					if (!fc_foreign_expr_walker((Node *) fc_cw->result,
											 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
						return false;
				}

				/* 递归到ELSE表达式。 */
				if (!fc_foreign_expr_walker((Node *) fc_ce->defresult,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 检测节点是否引入了不是源自于
				 * 外部变量的排序规则。 （如果是，我们只是暂时将其标记为不安全
				 * 而不是立即返回false，因为父节点
				 * 可能不在乎。）这与函数节点是一样的，
				 * 只是输入排序规则仅源于
				 * THEN和ELSE子表达式。
				 */
				fc_collation = fc_ce->casecollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_CaseTestExpr:
			{
				CaseTestExpr *fc_c = (CaseTestExpr *) fc_node;

				/* 如果我们似乎不在CASE参数WHEN内部，则直接返回。 */
				if (!fc_case_arg_cxt)
					return false;

				/*
				 * 否则，任何附加到
				 * CaseTestExpr节点的非默认排序规则必须源自CASE参数中的外部变量。
				 */
				fc_collation = fc_c->collation;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_case_arg_cxt->state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_case_arg_cxt->collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_ArrayExpr:
			{
				ArrayExpr  *fc_a = (ArrayExpr *) fc_node;

				/*
				 * 递归到输入子表达式。
				 */
				if (!fc_foreign_expr_walker((Node *) fc_a->elements,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * ArrayExpr不得引入不是源自于
				 * 输入外部变量的排序规则（与函数的逻辑相同）。
				 */
				fc_collation = fc_a->array_collid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		case T_List:
			{
				List	   *fc_l = (List *) fc_node;
				ListCell   *fc_lc;

				/*
				 * 递归到组件子表达式。
				 */
				foreach(fc_lc, fc_l)
				{
					if (!fc_foreign_expr_walker((Node *) lfirst(fc_lc),
											 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
						return false;
				}

				/*
				 * 处理列表时，排序状态将从列表元素中传递上去。
				 */
				fc_collation = fc_inner_cxt.collation;
				fc_state = fc_inner_cxt.state;

				/* 不要对列表应用exprType()。 */
				fc_check_type = false;
			}
			break;
		case T_Aggref:
			{
				Aggref	   *fc_agg = (Aggref *) fc_node;
				ListCell   *fc_lc;

				/* 在非分组上下文中不安全进行推送 */
				if (!IS_UPPER_REL(fc_glob_cxt->foreignrel))
					return false;

				/* 只有非分裂聚合是可以推送的。 */
				if (fc_agg->aggsplit != AGGSPLIT_SIMPLE)
					return false;

				/* 和往常一样，它必须是可运输的。 */
				if (!is_shippable(fc_agg->aggfnoid, ProcedureRelationId, fc_fpinfo))
					return false;

				/*
				 * 递归到输入参数。aggdirectargs、aggorder和
				 * aggdistinct都在参数中，因此不需要显式检查
				 * 它们的可运输性。
				 */
				foreach(fc_lc, fc_agg->args)
				{
					Node	   *fc_n = (Node *) lfirst(fc_lc);

					/* 如果是TargetEntry，从中提取表达式 */
					if (IsA(fc_n, TargetEntry))
					{
						TargetEntry *fc_tle = (TargetEntry *) fc_n;

						fc_n = (Node *) fc_tle->expr;
					}

					if (!fc_foreign_expr_walker(fc_n,
											 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
						return false;
				}

				/*
				 * 对于aggorder元素，检查排序运算符（如果
				 * 指定）是否可运输。
				 */
				if (fc_agg->aggorder)
				{
					ListCell   *fc_lc;

					foreach(fc_lc, fc_agg->aggorder)
					{
						SortGroupClause *fc_srt = (SortGroupClause *) lfirst(fc_lc);
						Oid			fc_sortcoltype;
						TypeCacheEntry *fc_typentry;
						TargetEntry *fc_tle;

						fc_tle = get_sortgroupref_tle(fc_srt->tleSortGroupRef,
												   fc_agg->args);
						fc_sortcoltype = exprType((Node *) fc_tle->expr);
						fc_typentry = lookup_type_cache(fc_sortcoltype,
													 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
						/* 检查非默认排序运算符的可运输性。 */
						if (fc_srt->sortop != fc_typentry->lt_opr &&
							fc_srt->sortop != fc_typentry->gt_opr &&
							!is_shippable(fc_srt->sortop, OperatorRelationId,
										  fc_fpinfo))
							return false;
					}
				}

				/* 检查聚合过滤器 */
				if (!fc_foreign_expr_walker((Node *) fc_agg->aggfilter,
										 fc_glob_cxt, &fc_inner_cxt, fc_case_arg_cxt))
					return false;

				/*
				 * 如果聚合的输入排序规则不是源自
				 * 外部变量，则无法发送到远程。
				 */
				if (fc_agg->inputcollid == InvalidOid)
					 /* OK, inputs are all noncollatable */ ;
				else if (fc_inner_cxt.state != FDW_COLLATE_SAFE ||
						 fc_agg->inputcollid != fc_inner_cxt.collation)
					return false;

				/*
				 * 检测节点是否引入了不源自外部 Var 的排序。
				 * （如果是这样，我们只是将其标记为不安全，而不是
				 * 立即返回 false，因为父节点可能不在乎。）
				 */
				fc_collation = fc_agg->aggcollid;
				if (fc_collation == InvalidOid)
					fc_state = FDW_COLLATE_NONE;
				else if (fc_inner_cxt.state == FDW_COLLATE_SAFE &&
						 fc_collation == fc_inner_cxt.collation)
					fc_state = FDW_COLLATE_SAFE;
				else if (fc_collation == DEFAULT_COLLATION_OID)
					fc_state = FDW_COLLATE_NONE;
				else
					fc_state = FDW_COLLATE_UNSAFE;
			}
			break;
		default:

			/*
			 * 如果是其他情况，则假设它是不安全的。这个列表可以在
			 * 后面扩展，但不要忘记在下面添加deparse支持。
			 */
			return false;
	}

	/*
	 * 如果给定表达式的结果类型不可传输，它就无法发送
	 * 到远程，因为它在远程侧可能具有不兼容的语义。
	 */
	if (fc_check_type && !is_shippable(exprType(fc_node), TypeRelationId, fc_fpinfo))
		return false;

	/*
	 * 现在，将我的排序信息合并到我父节点的状态中。
	 */
	if (fc_state > fc_outer_cxt->state)
	{
		/* 重写先前的父状态 */
		fc_outer_cxt->collation = fc_collation;
		fc_outer_cxt->state = fc_state;
	}
	else if (fc_state == fc_outer_cxt->state)
	{
		/* 合并，或在有排序冲突时检测错误 */
		switch (fc_state)
		{
			case FDW_COLLATE_NONE:
				/* 空 + 空 仍然是空 */
				break;
			case FDW_COLLATE_SAFE:
				if (fc_collation != fc_outer_cxt->collation)
				{
					/*
					 * 非默认排序总是优于默认排序。
					 */
					if (fc_outer_cxt->collation == DEFAULT_COLLATION_OID)
					{
						/* 重写先前的父状态 */
						fc_outer_cxt->collation = fc_collation;
					}
					else if (fc_collation != DEFAULT_COLLATION_OID)
					{
						/*
						 * 冲突；将状态显示为不确定。我们不
						 * 想立即“返回假”，因为父
						 * 节点可能不关心排序。
						 */
						fc_outer_cxt->state = FDW_COLLATE_UNSAFE;
					}
				}
				break;
			case FDW_COLLATE_UNSAFE:
				/* 我们仍然存在冲突... */
				break;
		}
	}

	/* 看起来正常 */
	return true;
}

/*
 * 如果给定的表达式是我们必须将其值发送到
 * 外部服务器的内容，则返回真。
 *
 * 当表达式是一个可传输节点，而deparseExpr将其添加到context->params_list时，这应返回真。请注意，我们不关心
 * 表达式是否*包含*这样的节点，只关心一个是否出现在
 * 顶层。我们需要这个来检测setrefs.c识别的情况，
 * 在这些情况下，fdw_exprs项（来自params_list）
 * 和fdw_scan_tlist中的一个条目之间存在假匹配（我们正在考虑将给定的
 * 表达式放入其中）。
 */
bool is_foreign_param(PlannerInfo *fc_root,
				 RelOptInfo *fc_baserel,
				 Expr *fc_expr)
{
	if (fc_expr == NULL)
		return false;

	switch (nodeTag(fc_expr))
	{
		case T_Var:
			{
				/* 除非它是外部 Var，不然就必须发送 */
				Var		   *fc_var = (Var *) fc_expr;
				PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) (fc_baserel->fdw_private);
				Relids		fc_relids;

				if (IS_UPPER_REL(fc_baserel))
					fc_relids = fc_fpinfo->outerrel->relids;
				else
					fc_relids = fc_baserel->relids;

				if (bms_is_member(fc_var->varno, fc_relids) && fc_var->varlevelsup == 0)
					return false;	/* 外部 Var，因此不是参数 */
				else
					return true;	/* 它必须是一个参数 */
				break;
			}
		case T_Param:
			/* 参数总是必须发送到外部服务器 */
			return true;
		default:
			break;
	}
	return false;
}

/*
 * 如果安全将由'pathkey'描述的排序表达式下推到外部服务器，则返回真。
 */
bool is_foreign_pathkey(PlannerInfo *fc_root,
				   RelOptInfo *fc_baserel,
				   PathKey *fc_pathkey)
{
	EquivalenceClass *fc_pathkey_ec = fc_pathkey->pk_eclass;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_baserel->fdw_private;

	/*
	 * is_foreign_expr也会检测易变表达式，但在这里检查
	 * ec_has_volatile可以节省一些周期。
	 */
	if (fc_pathkey_ec->ec_has_volatile)
		return false;

	/* 如果pathkey的opfamily不可传输，则无法下推排序 */
	if (!is_shippable(fc_pathkey->pk_opfamily, OperatorFamilyRelationId, fc_fpinfo))
		return false;

	/* 如果存在合适的EC成员，则可以下推 */
	return (find_em_for_rel(fc_root, fc_pathkey_ec, fc_baserel) != NULL);
}

/*
 * 将类型OID + typmod信息转换为我们可以发送到远程
 * 服务器的类型名称。其他地方最好已经验证过该类型名称
 * 在远程端是被期望知道的。
 *
 * 这几乎只是format_type_with_typemod()，除了如果让其自身处理，该函数将基于
 * 本地search_path做出模式限定决策，这是错误的。我们必须对所有不在pg_catalog中的
 * 类型名称进行模式限定。我们在这里假设内置类型
 * 都在pg_catalog中，不需要限定；否则，请进行限定。
 */
static char * fc_deparse_type_name(Oid fc_type_oid, int32 fc_typemod)
{
	bits16		fc_flags = FORMAT_TYPE_TYPEMOD_GIVEN;

	if (!is_builtin(fc_type_oid))
		fc_flags |= FORMAT_TYPE_FORCE_QUALIFY;

	return format_type_extended(fc_type_oid, fc_typemod, fc_flags);
}

/*
 * 为给定关系构建目标列表，以便解析为SELECT子句。
 *
 * 输出目标列表包含需要从
 * 外部服务器中获取的给定关系的列。如果foreignrel是一个上层关系，
 * 则输出目标列表也可以包含将在外部服务器上评估的表达式。
 */
List * build_tlist_to_deparse(RelOptInfo *fc_foreignrel)
{
	List	   *fc_tlist = NIL;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;
	ListCell   *fc_lc;

	/*
	 * 对于上层关系，我们已经在检查可传输性时构建了目标列表，
	 * 所以只需返回该列表即可。
	 */
	if (IS_UPPER_REL(fc_foreignrel))
		return fc_fpinfo->grouped_tlist;

	/*
	 * 我们需要在 foreignrel->reltarget->exprs 中指定的列和
	 * 评估本地条件所需的列。
	 */
	fc_tlist = add_to_flat_tlist(fc_tlist,
							  pull_var_clause((Node *) fc_foreignrel->reltarget->exprs,
											  PVC_RECURSE_PLACEHOLDERS));
	foreach(fc_lc, fc_fpinfo->local_conds)
	{
		RestrictInfo *fc_rinfo = lfirst_node(RestrictInfo, fc_lc);

		fc_tlist = add_to_flat_tlist(fc_tlist,
								  pull_var_clause((Node *) fc_rinfo->clause,
												  PVC_RECURSE_PLACEHOLDERS));
	}

	return fc_tlist;
}

/*
 * 将给定关系的 SELECT 语句反解析到 buf 中。
 *
 * tlist 包含要从外部服务器提取的所需列的列表。
 * 对于基础关系，使用 fpinfo->attrs_used 来构建 SELECT 子句，
 * 因此对于基础关系 tlist 被忽略。
 *
 * remote_conds 是要反解析到 WHERE 子句中的条件列表
 * （或者在上层关系的情况下，反解析到 HAVING 子句中）。
 *
 * 如果 params_list 不是 NULL，它将接收用于子句中的参数和其他关系
 * Vars 的 Params 列表；这些值必须作为参数值传输到远程
 * 服务器。
 *
 * 如果 params_list 是 NULL，我们正在生成 EXPLAIN 目的的查询，
 * 因此应将 Params 和其他关系 Vars 替换为虚拟值。
 *
 * pathkeys 是按顺序排列结果的 pathkeys 列表。
 *
 * is_subquery 是用于指示是否将指定关系反解析为子查询的标志。
 *
 * 选定的列列表在 retrieved_attrs 中返回。
 */
void deparseSelectStmtForRel(StringInfo fc_buf, PlannerInfo *fc_root, RelOptInfo *fc_rel,
						List *fc_tlist, List *fc_remote_conds, List *fc_pathkeys,
						bool fc_has_final_sort, bool fc_has_limit, bool fc_is_subquery,
						List **fc_retrieved_attrs, List **fc_params_list)
{
	deparse_expr_cxt fc_context;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_rel->fdw_private;
	List	   *fc_quals;

	/*
	 * 我们处理外部表的关系，以及它们与上层
	 * 关系之间的连接。
	 */
	Assert(IS_JOIN_REL(fc_rel) || IS_SIMPLE_REL(fc_rel) || IS_UPPER_REL(fc_rel));

	/* 填充上下文中与上层、连接和基础关系共有的部分 */
	fc_context.buf = fc_buf;
	fc_context.root = fc_root;
	fc_context.foreignrel = fc_rel;
	fc_context.scanrel = IS_UPPER_REL(fc_rel) ? fc_fpinfo->outerrel : fc_rel;
	fc_context.params_list = fc_params_list;

	/* 构建 SELECT 子句 */
	fc_deparseSelectSql(fc_tlist, fc_is_subquery, fc_retrieved_attrs, &fc_context);

	/*
	 * 对于上层关系，WHERE 子句是根据基础扫描关系的远程
	 * 条件构建的；否则，我们可以直接使用提供的远程条件列表。
	 */
	if (IS_UPPER_REL(fc_rel))
	{
		PgFdwRelationInfo *fc_ofpinfo;

		fc_ofpinfo = (PgFdwRelationInfo *) fc_fpinfo->outerrel->fdw_private;
		fc_quals = fc_ofpinfo->remote_conds;
	}
	else
		fc_quals = fc_remote_conds;

	/* 构建 FROM 和 WHERE 子句 */
	fc_deparseFromExpr(fc_quals, &fc_context);

	if (IS_UPPER_REL(fc_rel))
	{
		/* 附加 GROUP BY 子句 */
		fc_appendGroupByClause(fc_tlist, &fc_context);

		/* 如果找到任何有用的 pathkeys，则附加 ORDER BY 子句 */
		if (fc_remote_conds)
		{
			appendStringInfoString(fc_buf, " HAVING ");
			fc_appendConditions(fc_remote_conds, &fc_context);
		}
	}

	/* 如果必要，附加 LIMIT 子句 */
	if (fc_pathkeys)
		fc_appendOrderByClause(fc_pathkeys, fc_has_final_sort, &fc_context);

	/* 添加任何必要的 FOR UPDATE/SHARE。 */
	if (fc_has_limit)
		fc_appendLimitClause(&fc_context);

	/*
 * 构建一个简单的 SELECT 语句，检索指定外部表的所需列，
 * 并将其附加到 "buf"。输出仅包含 "SELECT ... "。
 *
 * 我们还创建一个整数列表来存储正在检索的列，这会返回到
 * *retrieved_attrs，除非我们将指定关系反解析为子查询。
 *
 * tlist 是所需列的列表。is_subquery 是用于指示
 * 是否将指定关系反解析为子查询的标志。
 * 有关详细信息，请参阅 deparseSelectStmtForRel() 的前言。
 */
	fc_deparseLockingClause(&fc_context);
}

/*
	 * 构建 SELECT 列表
	 */
static void fc_deparseSelectSql(List *fc_tlist, bool fc_is_subquery, List **fc_retrieved_attrs,
				 deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	RelOptInfo *fc_foreignrel = fc_context->foreignrel;
	PlannerInfo *fc_root = fc_context->root;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;

	/*
		 * 对于一个作为子查询反解析的关系，发出在
		 * 关系的 reltarget 中指定的表达式。请注意，由于这是为
		 * 子查询，因此无需关心 *retrieved_attrs。
		 */
	appendStringInfoString(fc_buf, "SELECT ");

	if (fc_is_subquery)
	{
		/*
		 * 对于连接或上层关系，输入 tlist 给出了
		 * 需要从外部服务器提取的列的列表。
		 */
		fc_deparseSubqueryTargetList(fc_context);
	}
	else if (IS_JOIN_REL(fc_foreignrel) || IS_UPPER_REL(fc_foreignrel))
	{
		/*
		 * 对于基础关系，fpinfo->attrs_used 给出了
		 * 需要从外部服务器提取的列的列表。
		 */
		fc_deparseExplicitTargetList(fc_tlist, false, fc_retrieved_attrs, fc_context);
	}
	else
	{
		/*
		 * 核心代码已经对每个正在规划的关系进行了某种锁定，因此我们
		 * 可以在这里使用 NoLock。
		 */
		RangeTblEntry *fc_rte = planner_rt_fetch(fc_foreignrel->relid, fc_root);

		/*
 * 构建 FROM 子句，并在需要时构建 WHERE 子句，并将其附加到
 * "buf"。
 *
 * quals 是要包含在 WHERE 子句中的条件列表。
 * （这些条件可能包含也可能不包含 RestrictInfo 装饰。）
 */
		Relation	fc_rel = table_open(fc_rte->relid, NoLock);

		fc_deparseTargetList(fc_buf, fc_rte, fc_foreignrel->relid, fc_rel, false,
						  fc_fpinfo->attrs_used, false, fc_retrieved_attrs);
		table_close(fc_rel, NoLock);
	}
}

/* 对于上层关系，scanrel 必须是 joinrel 或 baserel */
static void fc_deparseFromExpr(List *fc_quals, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	RelOptInfo *fc_scanrel = fc_context->scanrel;

	/* 构建 FROM 子句 */
	Assert(!IS_UPPER_REL(fc_context->foreignrel) ||
		   IS_JOIN_REL(fc_scanrel) || IS_SIMPLE_REL(fc_scanrel));

	/* 构建 WHERE 子句 */
	appendStringInfoString(fc_buf, " FROM ");
	fc_deparseFromExprForRel(fc_buf, fc_context->root, fc_scanrel,
						  (bms_membership(fc_scanrel->relids) == BMS_MULTIPLE),
						  (Index) 0, NULL, fc_context->params_list);

	/*
 * 发出一个目标列表，以检索 attrs_used 中指定的列。
 * 这用于 SELECT 和 RETURNING 目标列表；is_returning
 * 参数仅对 RETURNING 目标列表为真。
 *
 * tlist 文本附加到 buf，我们还创建一个整数列表
 * 以存储正在检索的列，并返回给 *retrieved_attrs。
 *
 * 如果 qualify_col 为真，则在列名称之前添加关系别名。
 */
	if (fc_quals != NIL)
	{
		appendStringInfoString(fc_buf, " WHERE ");
		fc_appendConditions(fc_quals, fc_context);
	}
}

/* 如果有完整行引用，则我们需要所有列。 */
static void fc_deparseTargetList(StringInfo fc_buf,
				  RangeTblEntry *fc_rte,
				  Index fc_rtindex,
				  Relation fc_rel,
				  bool fc_is_returning,
				  Bitmapset *fc_attrs_used,
				  bool fc_qualify_col,
				  List **fc_retrieved_attrs)
{
	TupleDesc	fc_tupdesc = RelationGetDescr(fc_rel);
	bool		fc_have_wholerow;
	bool		fc_first;
	int			fc_i;

	*fc_retrieved_attrs = NIL;

	/* 忽略已删除的属性。 */
	fc_have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
								  fc_attrs_used);

	fc_first = true;
	for (fc_i = 1; fc_i <= fc_tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i - 1);

		/*
	 * 如果需要，添加 ctid。我们目前不支持检索任何其他
	 * 系统列。
	 */
		if (fc_attr->attisdropped)
			continue;

		if (fc_have_wholerow ||
			bms_is_member(fc_i - FirstLowInvalidHeapAttributeNumber,
						  fc_attrs_used))
		{
			if (!fc_first)
				appendStringInfoString(fc_buf, ", ");
			else if (fc_is_returning)
				appendStringInfoString(fc_buf, " RETURNING ");
			fc_first = false;

			fc_deparseColumnRef(fc_buf, fc_rtindex, fc_i, fc_rte, fc_qualify_col);

			*fc_retrieved_attrs = lappend_int(*fc_retrieved_attrs, fc_i);
		}
	}

	/* 如果没有未删除的列，则不要生成不良语法 */
	if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber,
					  fc_attrs_used))
	{
		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		else if (fc_is_returning)
			appendStringInfoString(fc_buf, " RETURNING ");
		fc_first = false;

		if (fc_qualify_col)
			ADD_REL_QUALIFIER(fc_buf, fc_rtindex);
		appendStringInfoString(fc_buf, "ctid");

		*fc_retrieved_attrs = lappend_int(*fc_retrieved_attrs,
									   SelfItemPointerAttributeNumber);
	}

	/*
 * 对给定关系（context->scanrel）进行适当的锁定子句（FOR UPDATE 或 FOR SHARE）进行反解析。
 */
	if (fc_first && !fc_is_returning)
		appendStringInfoString(fc_buf, "NULL");
}

/*
		 * 如果关系出现在较低的子查询中，则忽略该关系。
		 * 这种关系的锁定子句如果必要会在子查询中包含。
		 */
static void fc_deparseLockingClause(deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	PlannerInfo *fc_root = fc_context->root;
	RelOptInfo *fc_rel = fc_context->scanrel;
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_rel->fdw_private;
	int			fc_relid = -1;

	while ((fc_relid = bms_next_member(fc_rel->relids, fc_relid)) >= 0)
	{
		/*
		 * 如果合适，则添加 FOR UPDATE/SHARE。我们在初始行获取期间应用锁定，
		 * 而不是像本地表那样稍后再进行。试图完全复制本地语义所涉及的额外往返
		 * 似乎没有价值（另见 RowMarkType 的注释）。
		 *
		 * 注意：因为我们实际上将查询作为光标运行，这假设
		 * DECLARE CURSOR ... FOR UPDATE 是支持的，而在 8.3 之前并不支持。
		 */
		if (bms_is_member(fc_relid, fc_fpinfo->lower_subquery_rels))
			continue;

		/* 关系是 UPDATE/DELETE 目标，因此使用 FOR UPDATE */
		if (bms_is_member(fc_relid, fc_root->all_result_relids) &&
			(fc_root->parse->commandType == CMD_UPDATE ||
			 fc_root->parse->commandType == CMD_DELETE))
		{
			/* 如果我们是在此处处理连接关系，则添加关系别名 */
			appendStringInfoString(fc_buf, " FOR UPDATE");

			/*
				 * 关系被指定为 FOR UPDATE/SHARE 目标，因此
				 * 处理它。（但我们也可能看到 LCS_NONE，意味着这
				 * 终究不是一个目标关系。）
				 *
				 * 目前，仅忽略任何 [NO] KEY 规范，因为 (a)
				 * 对于我们没有完整信息的远程表这意味着什么并不清楚，以及 (b)
				 * 在旧的远程服务器上无论如何都无法工作。同样，我们也不
				 * 担心 NOWAIT。
				 */
			if (IS_JOIN_REL(fc_rel))
				appendStringInfo(fc_buf, " OF %s%d", REL_ALIAS_PREFIX, fc_relid);
		}
		else
		{
			PlanRowMark *fc_rc = get_plan_rowmark(fc_root->rowMarks, fc_relid);

			if (fc_rc)
			{
				/* 不需要锁定 */
				switch (fc_rc->strength)
				{
					case LCS_NONE:
						/* 如果我们是在此处处理连接关系，则添加关系别名 */
						break;
					case LCS_FORKEYSHARE:
					case LCS_FORSHARE:
						appendStringInfoString(fc_buf, " FOR SHARE");
						break;
					case LCS_FORNOKEYUPDATE:
					case LCS_FORUPDATE:
						appendStringInfoString(fc_buf, " FOR UPDATE");
						break;
				}

				/*
				 * 关系被指定为 FOR UPDATE/SHARE 目标，因此
				 * 处理它。（但我们也可能看到 LCS_NONE，意味着这
				 * 终究不是一个目标关系。）
				 *
				 * 目前，仅忽略任何 [NO] KEY 规范，因为 (a)
				 * 对于我们没有完整信息的远程表这意味着什么并不清楚，以及 (b)
				 * 在旧的远程服务器上无论如何都无法工作。同样，我们也不
				 * 担心 NOWAIT。
				 */
				if (bms_membership(fc_rel->relids) == BMS_MULTIPLE &&
					fc_rc->strength != LCS_NONE)
					appendStringInfo(fc_buf, " OF %s%d", REL_ALIAS_PREFIX, fc_relid);
			}
		}
	}
}

/* 确保表达式中的任何常量以可移植的方式打印 */
static void fc_appendConditions(List *fc_exprs, deparse_expr_cxt *fc_context)
{
	int			fc_nestlevel;
	ListCell   *fc_lc;
	bool		fc_is_first = true;
	StringInfo	fc_buf = fc_context->buf;

	/* 如果需要，从RestrictInfo中提取子句 */
	fc_nestlevel = set_transmission_modes();

	foreach(fc_lc, fc_exprs)
	{
		Expr	   *fc_expr = (Expr *) lfirst(fc_lc);

		/* 用“AND”连接表达式并为每个条件加上括号。 */
		if (IsA(fc_expr, RestrictInfo))
			fc_expr = ((RestrictInfo *) fc_expr)->clause;

		/* 输出给定连接类型的连接名称 */
		if (!fc_is_first)
			appendStringInfoString(fc_buf, " AND ");

		appendStringInfoChar(fc_buf, '(');
		fc_deparseExpr(fc_expr, fc_context);
		appendStringInfoChar(fc_buf, ')');

		fc_is_first = false;
	}

	reset_transmission_modes(fc_nestlevel);
}

/* 不应该来到这里，但要防止错误的代码。 */
const char * get_jointype_name(JoinType fc_jointype)
{
	switch (fc_jointype)
	{
		case JOIN_INNER:
			return "INNER";

		case JOIN_LEFT:
			return "LEFT";

		case JOIN_RIGHT:
			return "RIGHT";

		case JOIN_FULL:
			return "FULL";

		default:
			/* 让编译器满意 */
			elog(ERROR, "unsupported join type %d", fc_jointype);
	}

	/*
 * 反解析给定的目标列表，并将其附加到context->buf。
 *
 * tlist是TargetEntry的列表，而TargetEntry又包含Var节点。
 *
 * retrieved_attrs是从1开始的连续递增整数列表。
 * 其条目数量与tlist相同。
 *
 * 这同时用于SELECT和RETURNING目标列表；is_returning参数
 * 仅在RETURNING目标列表中为真。
 */
	return NULL;
}

/*
 * 输出给定关系的reltarget中指定的表达式。
 *
 * 这用于将给定关系作为子查询进行反解析。
 */
static void fc_deparseExplicitTargetList(List *fc_tlist,
						  bool fc_is_returning,
						  List **fc_retrieved_attrs,
						  deparse_expr_cxt *fc_context)
{
	ListCell   *fc_lc;
	StringInfo	fc_buf = fc_context->buf;
	int			fc_i = 0;

	*fc_retrieved_attrs = NIL;

	foreach(fc_lc, fc_tlist)
	{
		TargetEntry *fc_tle = lfirst_node(TargetEntry, fc_lc);

		if (fc_i > 0)
			appendStringInfoString(fc_buf, ", ");
		else if (fc_is_returning)
			appendStringInfoString(fc_buf, " RETURNING ");

		fc_deparseExpr((Expr *) fc_tle->expr, fc_context);

		*fc_retrieved_attrs = lappend_int(*fc_retrieved_attrs, fc_i + 1);
		fc_i++;
	}

	if (fc_i == 0 && !fc_is_returning)
		appendStringInfoString(fc_buf, "NULL");
}

/* 仅在这些情况下调用。 */
static void fc_deparseSubqueryTargetList(deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	RelOptInfo *fc_foreignrel = fc_context->foreignrel;
	bool		fc_first;
	ListCell   *fc_lc;

	/* 如果没有表达式，不生成糟糕的语法 */
	Assert(IS_SIMPLE_REL(fc_foreignrel) || IS_JOIN_REL(fc_foreignrel));

	fc_first = true;
	foreach(fc_lc, fc_foreignrel->reltarget->exprs)
	{
		Node	   *fc_node = (Node *) lfirst(fc_lc);

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		fc_deparseExpr((Expr *) fc_node, fc_context);
	}

	/*
 * 为给定关系构造FROM子句
 *
 * 该函数为连接关系构造... JOIN ... ON ...。对于基本
 * 关系，它仅返回带有适当别名的模式合格的表名，
 * 如果请求的话。
 *
 * 'ignore_rel'要么为零，要么为目标关系的RT索引。 在
 * 后一种情况下，该函数构造UPDATE的FROM子句或DELETE的USING子句；
 * 它将连接关系反解析为好像该关系从未包含
 * 目标关系，并创建一个条件列表以反解析成
 * 顶层WHERE子句，返回给*ignore_conds。
 */
	if (fc_first)
		appendStringInfoString(fc_buf, "NULL");
}

/*
			 * 如果这是内连接，将joinclauses添加到*ignore_conds并
			 * 将其设置为空，以便这些条件可以被反解析到
			 * WHERE子句中。请注意，由于目标关系永远
			 * 不能位于外连接的可空侧，因此可以安全地
			 * 将这些条件提升到WHERE子句中（请参见foreign_join_ok()）。
			 * 还请注意，由于目标关系仅与查询中的任何其他
			 * 关系进行内连接，所有提及目标关系的连接
			 * 树中的条件都可以通过递归方式反解析到
			 * WHERE子句中。
			 */
static void fc_deparseFromExprForRel(StringInfo fc_buf, PlannerInfo *fc_root, RelOptInfo *fc_foreignrel,
					  bool fc_use_alias, Index fc_ignore_rel, List **fc_ignore_conds,
					  List **fc_params_list)
{
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;

	if (IS_JOIN_REL(fc_foreignrel))
	{
		StringInfoData fc_join_sql_o;
		StringInfoData fc_join_sql_i;
		RelOptInfo *fc_outerrel = fc_fpinfo->outerrel;
		RelOptInfo *fc_innerrel = fc_fpinfo->innerrel;
		bool		fc_outerrel_is_target = false;
		bool		fc_innerrel_is_target = false;

		if (fc_ignore_rel > 0 && bms_is_member(fc_ignore_rel, fc_foreignrel->relids))
		{
			/*
			 * 检查输入的关系是否是目标关系。
			 */
			if (fc_fpinfo->jointype == JOIN_INNER)
			{
				*fc_ignore_conds = list_concat(*fc_ignore_conds,
											fc_fpinfo->joinclauses);
				fc_fpinfo->joinclauses = NIL;
			}

			/* 如果不是目标关系，则反解析外部关系。 */
			if (fc_outerrel->relid == fc_ignore_rel)
				fc_outerrel_is_target = true;
			else if (fc_innerrel->relid == fc_ignore_rel)
				fc_innerrel_is_target = true;
		}

		/*
			 * 如果内部关系是目标关系，则跳过对其的反解析。
			 * 请注意，由于目标关系与查询中的任何其他
			 * 关系的连接是内连接，并且永远不能位于
			 * 外连接的可空侧，因此可以将连接与较高层的
			 * 连接进行互换（参见src/backend/optimizer/README中
			 * 显示的外连接重排序的恒等式1），这意味着
			 * 在这里跳过目标关系的反解析是安全的。
			 */
		if (!fc_outerrel_is_target)
		{
			initStringInfo(&fc_join_sql_o);
			fc_deparseRangeTblRef(&fc_join_sql_o, fc_root, fc_outerrel,
							   fc_fpinfo->make_outerrel_subquery,
							   fc_ignore_rel, fc_ignore_conds, fc_params_list);

			/* 如果不是目标关系，则反解析内部关系。 */
			if (fc_innerrel_is_target)
			{
				Assert(fc_fpinfo->jointype == JOIN_INNER);
				Assert(fc_fpinfo->joinclauses == NIL);
				appendBinaryStringInfo(fc_buf, fc_join_sql_o.data, fc_join_sql_o.len);
				return;
			}
		}

		/*
			 * 如果外部关系是目标关系，跳过对其的去解析。
			 * 见上面的安全性说明。
			 */
		if (!fc_innerrel_is_target)
		{
			initStringInfo(&fc_join_sql_i);
			fc_deparseRangeTblRef(&fc_join_sql_i, fc_root, fc_innerrel,
							   fc_fpinfo->make_innerrel_subquery,
							   fc_ignore_rel, fc_ignore_conds, fc_params_list);

			/* 两个关系都不是目标关系。 */
			if (fc_outerrel_is_target)
			{
				Assert(fc_fpinfo->jointype == JOIN_INNER);
				Assert(fc_fpinfo->joinclauses == NIL);
				appendBinaryStringInfo(fc_buf, fc_join_sql_i.data, fc_join_sql_i.len);
				return;
			}
		}

		/*
		 * 对于一个连接关系，FROM 子句条目去解析为
		 *
		 * ((外部关系) <连接类型> (内部关系) ON (连接条件))
		 */
		Assert(!fc_outerrel_is_target && !fc_innerrel_is_target);

		/* 附加连接条件；如果没有连接条件，则为（TRUE） */
		appendStringInfo(fc_buf, "(%s %s JOIN %s ON ", fc_join_sql_o.data,
						 get_jointype_name(fc_fpinfo->jointype), fc_join_sql_i.data);

		/* 结束 FROM 子句条目。 */
		if (fc_fpinfo->joinclauses)
		{
			deparse_expr_cxt fc_context;

			fc_context.buf = fc_buf;
			fc_context.foreignrel = fc_foreignrel;
			fc_context.scanrel = fc_foreignrel;
			fc_context.root = fc_root;
			fc_context.params_list = fc_params_list;

			appendStringInfoChar(fc_buf, '(');
			fc_appendConditions(fc_fpinfo->joinclauses, &fc_context);
			appendStringInfoChar(fc_buf, ')');
		}
		else
			appendStringInfoString(fc_buf, "(TRUE)");

		/*
		 * 核心代码已经对每个正在规划的关系有一些锁定，所以我们
		 * 可以在这里使用 NoLock。
		 */
		appendStringInfoChar(fc_buf, ')');
	}
	else
	{
		RangeTblEntry *fc_rte = planner_rt_fetch(fc_foreignrel->relid, fc_root);

		/*
 * 构建 FROM 子句，并在需要时构建 WHERE 子句，并将其附加到
 * "buf"。
 *
 * quals 是要包含在 WHERE 子句中的条件列表。
 * （这些条件可能包含也可能不包含 RestrictInfo 装饰。）
 */
		Relation	fc_rel = table_open(fc_rte->relid, NoLock);

		fc_deparseRelation(fc_buf, fc_rel);

		/*
 * 将给定关系的 FROM 子句条目附加到 buf 中。
 */
		if (fc_use_alias)
			appendStringInfo(fc_buf, " %s%d", REL_ALIAS_PREFIX, fc_foreignrel->relid);

		table_close(fc_rel, NoLock);
	}
}

/* 仅应在这些情况下调用。 */
static void fc_deparseRangeTblRef(StringInfo fc_buf, PlannerInfo *fc_root, RelOptInfo *fc_foreignrel,
				   bool fc_make_subquery, Index fc_ignore_rel, List **fc_ignore_conds,
				   List **fc_params_list)
{
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;

	/* 如果没有表达式，不生成糟糕的语法 */
	Assert(IS_SIMPLE_REL(fc_foreignrel) || IS_JOIN_REL(fc_foreignrel));

	Assert(fc_fpinfo->local_conds == NIL);

	/*
		 * 给定关系不应包含目标关系，因为
		 * 这仅应发生在完全连接的输入关系中，而
		 * 这样的关系绝对不能包含 UPDATE/DELETE 目标。
		 */
	if (fc_make_subquery)
	{
		List	   *fc_retrieved_attrs;
		int			fc_ncols;

		/* 去解析表示关系的子查询。 */
		Assert(fc_ignore_rel == 0 ||
			   !bms_is_member(fc_ignore_rel, fc_foreignrel->relids));

		/* 附加关系别名。 */
		appendStringInfoChar(fc_buf, '(');
		deparseSelectStmtForRel(fc_buf, fc_root, fc_foreignrel, NIL,
								fc_fpinfo->remote_conds, NIL,
								false, false, true,
								&fc_retrieved_attrs, fc_params_list);
		appendStringInfoChar(fc_buf, ')');

		/*
		 * 如有必要，附加列别名。注意，子查询发出
		 * 在关系的 reltarget 中指定的表达式（参见
		 * deparseSubqueryTargetList）。
		 */
		appendStringInfo(fc_buf, " %s%d", SUBQUERY_REL_ALIAS_PREFIX,
						 fc_fpinfo->relation_index);

		/*
 * 去解析远程 INSERT 语句
 *
 * 语句文本追加到 buf，并且我们还创建了一个整数列表
 * 用于通过 WITH CHECK OPTION 或 RETURNING 获取的列（如果有），
 * 返回给 *retrieved_attrs。
 *
 * 这还存储 VALUES 子句的结束位置，以便我们可以
 * 之后重建一批行的 INSERT。
 */
		fc_ncols = list_length(fc_foreignrel->reltarget->exprs);
		if (fc_ncols > 0)
		{
			int			fc_i;

			appendStringInfoChar(fc_buf, '(');
			for (fc_i = 1; fc_i <= fc_ncols; fc_i++)
			{
				if (fc_i > 1)
					appendStringInfoString(fc_buf, ", ");

				appendStringInfo(fc_buf, "%s%d", SUBQUERY_COL_ALIAS_PREFIX, fc_i);
			}
			appendStringInfoChar(fc_buf, ')');
		}
	}
	else
		fc_deparseFromExprForRel(fc_buf, fc_root, fc_foreignrel, true, fc_ignore_rel,
							  fc_ignore_conds, fc_params_list);
}

/*
 * 重建远程 INSERT 语句
 *
 * 提供一批中的行数，构建具有正确参数数量的 INSERT 语句。
 */
void deparseInsertSql(StringInfo fc_buf, RangeTblEntry *fc_rte,
				 Index fc_rtindex, Relation fc_rel,
				 List *fc_targetAttrs, bool fc_doNothing,
				 List *fc_withCheckOptionList, List *fc_returningList,
				 List **fc_retrieved_attrs, int *fc_values_end_len)
{
	TupleDesc	fc_tupdesc = RelationGetDescr(fc_rel);
	AttrNumber	fc_pindex;
	bool		fc_first;
	ListCell   *fc_lc;

	appendStringInfoString(fc_buf, "INSERT INTO ");
	fc_deparseRelation(fc_buf, fc_rel);

	if (fc_targetAttrs)
	{
		appendStringInfoChar(fc_buf, '(');

		fc_first = true;
		foreach(fc_lc, fc_targetAttrs)
		{
			int			fc_attnum = lfirst_int(fc_lc);

			if (!fc_first)
				appendStringInfoString(fc_buf, ", ");
			fc_first = false;

			fc_deparseColumnRef(fc_buf, fc_rtindex, fc_attnum, fc_rte, false);
		}

		appendStringInfoString(fc_buf, ") VALUES (");

		fc_pindex = 1;
		fc_first = true;
		foreach(fc_lc, fc_targetAttrs)
		{
			int			fc_attnum = lfirst_int(fc_lc);
			Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_attnum - 1);

			if (!fc_first)
				appendStringInfoString(fc_buf, ", ");
			fc_first = false;

			if (fc_attr->attgenerated)
				appendStringInfoString(fc_buf, "DEFAULT");
			else
			{
				appendStringInfo(fc_buf, "$%d", fc_pindex);
				fc_pindex++;
			}
		}

		appendStringInfoChar(fc_buf, ')');
	}
	else
		appendStringInfoString(fc_buf, " DEFAULT VALUES");
	*fc_values_end_len = fc_buf->len;

	if (fc_doNothing)
		appendStringInfoString(fc_buf, " ON CONFLICT DO NOTHING");

	fc_deparseReturningList(fc_buf, fc_rte, fc_rtindex, fc_rel,
						 fc_rel->trigdesc && fc_rel->trigdesc->trig_insert_after_row,
						 fc_withCheckOptionList, fc_returningList, fc_retrieved_attrs);
}

/* 确保 values_end_len 合理 */
void rebuildInsertSql(StringInfo fc_buf, Relation fc_rel,
				 char *fc_orig_query, List *fc_target_attrs,
				 int fc_values_end_len, int fc_num_params,
				 int fc_num_rows)
{
	TupleDesc	fc_tupdesc = RelationGetDescr(fc_rel);
	int			fc_i;
	int			fc_pindex;
	bool		fc_first;
	ListCell   *fc_lc;

	/* 从原始查询中复制到第一条记录的结束位置 */
	Assert((fc_values_end_len > 0) && (fc_values_end_len <= strlen(fc_orig_query)));

	/*
	 * 向 VALUES 子句添加记录（我们已经为第一行有参数，
	 * 所以从正确的偏移量开始）。
	 */
	appendBinaryStringInfo(fc_buf, fc_orig_query, fc_values_end_len);

	/* 从原始查询中复制 VALUES 子句之后的内容 */
	fc_pindex = fc_num_params + 1;
	for (fc_i = 0; fc_i < fc_num_rows; fc_i++)
	{
		appendStringInfoString(fc_buf, ", (");

		fc_first = true;
		foreach(fc_lc, fc_target_attrs)
		{
			int			fc_attnum = lfirst_int(fc_lc);
			Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_attnum - 1);

			if (!fc_first)
				appendStringInfoString(fc_buf, ", ");
			fc_first = false;

			if (fc_attr->attgenerated)
				appendStringInfoString(fc_buf, "DEFAULT");
			else
			{
				appendStringInfo(fc_buf, "$%d", fc_pindex);
				fc_pindex++;
			}
		}

		appendStringInfoChar(fc_buf, ')');
	}

	/*
 * 去解析远程 UPDATE 语句
 *
 * 语句文本追加到 buf，并且我们还创建了一个整数列表
 * 用于通过 WITH CHECK OPTION 或 RETURNING 获取的列（如果有），
 * 返回给 *retrieved_attrs。
 */
	appendStringInfoString(fc_buf, fc_orig_query + fc_values_end_len);
}

/* ctid 始终是第一个参数 */
void deparseUpdateSql(StringInfo fc_buf, RangeTblEntry *fc_rte,
				 Index fc_rtindex, Relation fc_rel,
				 List *fc_targetAttrs,
				 List *fc_withCheckOptionList, List *fc_returningList,
				 List **fc_retrieved_attrs)
{
	TupleDesc	fc_tupdesc = RelationGetDescr(fc_rel);
	AttrNumber	fc_pindex;
	bool		fc_first;
	ListCell   *fc_lc;

	appendStringInfoString(fc_buf, "UPDATE ");
	fc_deparseRelation(fc_buf, fc_rel);
	appendStringInfoString(fc_buf, " SET ");

	fc_pindex = 2;					/*
 * 反解析远程 UPDATE 语句
 *
 * 'buf' 是用于追加语句的输出缓冲区
 * 'rtindex' 是关联目标关系的 RT 索引
 * 'rel' 是目标关系的关系描述符
 * 'foreignrel' 是目标关系或连接关系的 RelOptInfo
 *		包含查询中的所有基础关系
 * 'targetlist' 是底层外部扫描计划节点的 tlist
 *		（注意这仅包含新值表达式和垃圾属性）
 * 'targetAttrs' 是 UPDATE 的目标列
 * 'remote_conds' 是必须在远程评估的 qual 子句
 * '*params_list' 是一个输出表达式列表，将变为远程参数
 * 'returningList' 是 RETURNING 目标列表
 * '*retrieved_attrs' 是一个输出整数列表，表示通过 RETURNING 获取的列
 *		（如果有的话）
 */
	fc_first = true;
	foreach(fc_lc, fc_targetAttrs)
	{
		int			fc_attnum = lfirst_int(fc_lc);
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_attnum - 1);

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		fc_deparseColumnRef(fc_buf, fc_rtindex, fc_attnum, fc_rte, false);
		if (fc_attr->attgenerated)
			appendStringInfoString(fc_buf, " = DEFAULT");
		else
		{
			appendStringInfo(fc_buf, " = $%d", fc_pindex);
			fc_pindex++;
		}
	}
	appendStringInfoString(fc_buf, " WHERE ctid = $1");

	fc_deparseReturningList(fc_buf, fc_rte, fc_rtindex, fc_rel,
						 fc_rel->trigdesc && fc_rel->trigdesc->trig_update_after_row,
						 fc_withCheckOptionList, fc_returningList, fc_retrieved_attrs);
}

/* 设置递归的上下文结构 */
void deparseDirectUpdateSql(StringInfo fc_buf, PlannerInfo *fc_root,
					   Index fc_rtindex, Relation fc_rel,
					   RelOptInfo *fc_foreignrel,
					   List *fc_targetlist,
					   List *fc_targetAttrs,
					   List *fc_remote_conds,
					   List **fc_params_list,
					   List *fc_returningList,
					   List **fc_retrieved_attrs)
{
	deparse_expr_cxt fc_context;
	int			fc_nestlevel;
	bool		fc_first;
	RangeTblEntry *fc_rte = planner_rt_fetch(fc_rtindex, fc_root);
	ListCell   *fc_lc,
			   *fc_lc2;

	/* 确保表达式中的任何常量都是可移植打印的 */
	fc_context.root = fc_root;
	fc_context.foreignrel = fc_foreignrel;
	fc_context.scanrel = fc_foreignrel;
	fc_context.buf = fc_buf;
	fc_context.params_list = fc_params_list;

	appendStringInfoString(fc_buf, "UPDATE ");
	fc_deparseRelation(fc_buf, fc_rel);
	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
		appendStringInfo(fc_buf, " %s%d", REL_ALIAS_PREFIX, fc_rtindex);
	appendStringInfoString(fc_buf, " SET ");

	/* 如果需要，从RestrictInfo中提取子句 */
	fc_nestlevel = set_transmission_modes();

	fc_first = true;
	forboth(fc_lc, fc_targetlist, fc_lc2, fc_targetAttrs)
	{
		TargetEntry *fc_tle = lfirst_node(TargetEntry, fc_lc);
		int			fc_attnum = lfirst_int(fc_lc2);

		/*
 * 反解析远程 DELETE 语句
 *
 * 语句文本附加到 buf，我们还创建一个整数列表
 * 表示通过 RETURNING 获取的列（如果有的话），该列表返回
 * 给 *retrieved_attrs。
 */
		Assert(!fc_tle->resjunk);

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		fc_deparseColumnRef(fc_buf, fc_rtindex, fc_attnum, fc_rte, false);
		appendStringInfoString(fc_buf, " = ");
		fc_deparseExpr((Expr *) fc_tle->expr, &fc_context);
	}

	reset_transmission_modes(fc_nestlevel);

	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
	{
		List	   *fc_ignore_conds = NIL;

		appendStringInfoString(fc_buf, " FROM ");
		fc_deparseFromExprForRel(fc_buf, fc_root, fc_foreignrel, true, fc_rtindex,
							  &fc_ignore_conds, fc_params_list);
		fc_remote_conds = list_concat(fc_remote_conds, fc_ignore_conds);
	}

	if (fc_remote_conds)
	{
		appendStringInfoString(fc_buf, " WHERE ");
		fc_appendConditions(fc_remote_conds, &fc_context);
	}

	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
		fc_deparseExplicitTargetList(fc_returningList, true, fc_retrieved_attrs,
								  &fc_context);
	else
		fc_deparseReturningList(fc_buf, fc_rte, fc_rtindex, fc_rel, false,
							 NIL, fc_returningList, fc_retrieved_attrs);
}

/*
 * 反解析远程 DELETE 语句
 *
 * 'buf' 是用于追加语句的输出缓冲区
 * 'rtindex' 是关联目标关系的 RT 索引
 * 'rel' 是目标关系的关系描述符
 * 'foreignrel' 是目标关系或连接关系的 RelOptInfo
 *		包含查询中的所有基础关系
 * 'remote_conds' 是必须在远程评估的 qual 子句
 * '*params_list' 是一个输出表达式列表，将变为远程参数
 * 'returningList' 是 RETURNING 目标列表
 * '*retrieved_attrs' 是一个输出整数列表，表示通过 RETURNING 获取的列
 *		（如果有的话）
 */
void deparseDeleteSql(StringInfo fc_buf, RangeTblEntry *fc_rte,
				 Index fc_rtindex, Relation fc_rel,
				 List *fc_returningList,
				 List **fc_retrieved_attrs)
{
	appendStringInfoString(fc_buf, "DELETE FROM ");
	fc_deparseRelation(fc_buf, fc_rel);
	appendStringInfoString(fc_buf, " WHERE ctid = $1");

	fc_deparseReturningList(fc_buf, fc_rte, fc_rtindex, fc_rel,
						 fc_rel->trigdesc && fc_rel->trigdesc->trig_delete_after_row,
						 NIL, fc_returningList, fc_retrieved_attrs);
}

/* 设置递归的上下文结构 */
void deparseDirectDeleteSql(StringInfo fc_buf, PlannerInfo *fc_root,
					   Index fc_rtindex, Relation fc_rel,
					   RelOptInfo *fc_foreignrel,
					   List *fc_remote_conds,
					   List **fc_params_list,
					   List *fc_returningList,
					   List **fc_retrieved_attrs)
{
	deparse_expr_cxt fc_context;

	/* 确保表达式中的任何常量都是可移植打印的 */
	fc_context.root = fc_root;
	fc_context.foreignrel = fc_foreignrel;
	fc_context.scanrel = fc_foreignrel;
	fc_context.buf = fc_buf;
	fc_context.params_list = fc_params_list;

	appendStringInfoString(fc_buf, "DELETE FROM ");
	fc_deparseRelation(fc_buf, fc_rel);
	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
		appendStringInfo(fc_buf, " %s%d", REL_ALIAS_PREFIX, fc_rtindex);

	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
	{
		List	   *fc_ignore_conds = NIL;

		appendStringInfoString(fc_buf, " USING ");
		fc_deparseFromExprForRel(fc_buf, fc_root, fc_foreignrel, true, fc_rtindex,
							  &fc_ignore_conds, fc_params_list);
		fc_remote_conds = list_concat(fc_remote_conds, fc_ignore_conds);
	}

	if (fc_remote_conds)
	{
		appendStringInfoString(fc_buf, " WHERE ");
		fc_appendConditions(fc_remote_conds, &fc_context);
	}

	if (fc_foreignrel->reloptkind == RELOPT_JOINREL)
		fc_deparseExplicitTargetList(fc_returningList, true, fc_retrieved_attrs,
								  &fc_context);
	else
		fc_deparseReturningList(fc_buf, planner_rt_fetch(fc_rtindex, fc_root),
							 fc_rtindex, fc_rel, false,
							 NIL, fc_returningList, fc_retrieved_attrs);
}

/* 整行引用获取所有非系统列 */
static void fc_deparseReturningList(StringInfo fc_buf, RangeTblEntry *fc_rte,
					 Index fc_rtindex, Relation fc_rel,
					 bool fc_trig_after_row,
					 List *fc_withCheckOptionList,
					 List *fc_returningList,
					 List **fc_retrieved_attrs)
{
	Bitmapset  *fc_attrs_used = NULL;

	if (fc_trig_after_row)
	{
		/*
		 * 我们需要在本地查询的 WITH CHECK OPTION 列表中提到的属性，
		 * 包括非系统和系统属性。
		 *
		 * 注意：我们这样做是为了确保 WCO 约束将在远程实际插入/更新的数据上进行评估，
		 * 这些数据可能与核心代码提供的数据不同，例如
		 * 由于远程触发器的结果。
		 */
		fc_attrs_used =
			bms_make_singleton(0 - FirstLowInvalidHeapAttributeNumber);
	}

	if (fc_withCheckOptionList != NIL)
	{
		/*
		 * 我们需要在本地查询的 RETURNING 列表中提到的属性，
		 * 包括非系统和系统属性。
		 */
		pull_varattnos((Node *) fc_withCheckOptionList, fc_rtindex,
					   &fc_attrs_used);
	}

	if (fc_returningList != NIL)
	{
		/*
 * 构造 SELECT 语句以获取给定关系的块大小。
 *
 * 注意：我们使用本地定义的块大小，而不是远程定义。
 * 这可能是有争议的。
 *
 * 注意：pg_relation_size() 在 8.1 及以后版本中存在。
 */
		pull_varattnos((Node *) fc_returningList, fc_rtindex,
					   &fc_attrs_used);
	}

	if (fc_attrs_used != NULL)
		fc_deparseTargetList(fc_buf, fc_rte, fc_rtindex, fc_rel, true, fc_attrs_used, false,
						  fc_retrieved_attrs);
	else
		*fc_retrieved_attrs = NIL;
}

/* 我们需要远程关系名称作为文字。 */
void deparseAnalyzeSizeSql(StringInfo fc_buf, Relation fc_rel)
{
	StringInfoData fc_relname;

	/*
 * 构造SELECT语句以获取给定关系的示例行。
 *
 * SELECT命令附加到buf，检索到的列列表返回到*retrieved_attrs。
 */
	initStringInfo(&fc_relname);
	fc_deparseRelation(&fc_relname, fc_rel);

	appendStringInfoString(fc_buf, "SELECT pg_catalog.pg_relation_size(");
	deparseStringLiteral(fc_buf, fc_relname.data);
	appendStringInfo(fc_buf, "::pg_catalog.regclass) / %d", BLCKSZ);
}

/* 忽略被删除的列。 */
void deparseAnalyzeSql(StringInfo fc_buf, Relation fc_rel, List **fc_retrieved_attrs)
{
	Oid			fc_relid = RelationGetRelid(fc_rel);
	TupleDesc	fc_tupdesc = RelationGetDescr(fc_rel);
	int			fc_i;
	char	   *fc_colname;
	List	   *fc_options;
	ListCell   *fc_lc;
	bool		fc_first = true;

	*fc_retrieved_attrs = NIL;

	appendStringInfoString(fc_buf, "SELECT ");
	for (fc_i = 0; fc_i < fc_tupdesc->natts; fc_i++)
	{
		/* 使用属性名称或column_name选项。 */
		if (TupleDescAttr(fc_tupdesc, fc_i)->attisdropped)
			continue;

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		/* 对于零列关系不要生成错误语法。 */
		fc_colname = NameStr(TupleDescAttr(fc_tupdesc, fc_i)->attname);
		fc_options = GetForeignColumnOptions(fc_relid, fc_i + 1);

		foreach(fc_lc, fc_options)
		{
			DefElem    *fc_def = (DefElem *) lfirst(fc_lc);

			if (strcmp(fc_def->defname, "column_name") == 0)
			{
				fc_colname = defGetString(fc_def);
				break;
			}
		}

		appendStringInfoString(fc_buf, quote_identifier(fc_colname));

		*fc_retrieved_attrs = lappend_int(*fc_retrieved_attrs, fc_i + 1);
	}

	/*
	 * 构造FROM子句
	 */
	if (fc_first)
		appendStringInfoString(fc_buf, "NULL");

	/*
 * 构造一个简单的"TRUNCATE rel"语句
 */
	appendStringInfoString(fc_buf, " FROM ");
	fc_deparseRelation(fc_buf, fc_rel);
}

/*
 * 构造给定列的名称，并将其发出到buf中。
 * 如果它有column_name FDW选项，则使用该值而不是属性名称。
 *
 * 如果qualify_col为真，则使用关系的别名限定列名。
 */
void deparseTruncateSql(StringInfo fc_buf,
				   List *fc_rels,
				   DropBehavior fc_behavior,
				   bool fc_restart_seqs)
{
	ListCell   *fc_cell;

	appendStringInfoString(fc_buf, "TRUNCATE ");

	foreach(fc_cell, fc_rels)
	{
		Relation	fc_rel = lfirst(fc_cell);

		if (fc_cell != list_head(fc_rels))
			appendStringInfoString(fc_buf, ", ");

		fc_deparseRelation(fc_buf, fc_rel);
	}

	appendStringInfo(fc_buf, " %s IDENTITY",
					 fc_restart_seqs ? "RESTART" : "CONTINUE");

	if (fc_behavior == DROP_RESTRICT)
		appendStringInfoString(fc_buf, " RESTRICT");
	else if (fc_behavior == DROP_CASCADE)
		appendStringInfoString(fc_buf, " CASCADE");
}

/* 我们支持获取远程端的CTID和OID。 */
static void fc_deparseColumnRef(StringInfo fc_buf, int fc_varno, int fc_varattno, RangeTblEntry *fc_rte,
				 bool fc_qualify_col)
{
	/*
		 * 除了表OID外，所有其他系统属性都作为0获取，
		 * 表OID作为本地表OID获取。然而，我们必须小心；
		 * 表可能位于外连接下，在这种情况下，只要其他行的内容为NULL，它也必须为NULL。
		 */
	if (fc_varattno == SelfItemPointerAttributeNumber)
	{
		if (fc_qualify_col)
			ADD_REL_QUALIFIER(fc_buf, fc_varno);
		appendStringInfoString(fc_buf, "ctid");
	}
	else if (fc_varattno < 0)
	{
		/* 整行引用 */
		Oid			fc_fetchval = 0;

		if (fc_varattno == TableOidAttributeNumber)
			fc_fetchval = fc_rte->relid;

		if (fc_qualify_col)
		{
			appendStringInfoString(fc_buf, "CASE WHEN (");
			ADD_REL_QUALIFIER(fc_buf, fc_varno);
			appendStringInfo(fc_buf, "*)::text IS NOT NULL THEN %u END", fc_fetchval);
		}
		else
			appendStringInfo(fc_buf, "%u", fc_fetchval);
	}
	else if (fc_varattno == 0)
	{
		/* 仅在需要传递给deparseTargetList()时需要。 */
		Relation	fc_rel;
		Bitmapset  *fc_attrs_used;

		/*
		 * 关系上的锁将由上层调用方持有，因此在此处打开时无需锁定。
		 */
		List	   *fc_retrieved_attrs;

		/*
		 * 外部表的本地名称无法被外部服务器识别，并且它在外部服务器上引用的表
		 * 可能具有不同的列顺序或不同的列，这些列在本地声明时可能不同。
		 * 因此，我们必须将整行引用解析为ROW（本地引用的列）。
		 * 通过解析一个“整行”属性来构造这个。
		 */
		fc_rel = table_open(fc_rte->relid, NoLock);

		/*
		 * 如果整行引用位于外连接下，那么只要其余行变为NULL，它也必须变为NULL。
		 * 解析连接查询将始终涉及多个关系，因此qualify_col将为真。
		 */
		fc_attrs_used = bms_add_member(NULL,
									0 - FirstLowInvalidHeapAttributeNumber);

		/* 完成上述开始的CASE WHEN语句。 */
		if (fc_qualify_col)
		{
			appendStringInfoString(fc_buf, "CASE WHEN (");
			ADD_REL_QUALIFIER(fc_buf, fc_varno);
			appendStringInfoString(fc_buf, "*)::text IS NOT NULL THEN ");
		}

		appendStringInfoString(fc_buf, "ROW(");
		fc_deparseTargetList(fc_buf, fc_rte, fc_varno, fc_rel, false, fc_attrs_used, fc_qualify_col,
						  &fc_retrieved_attrs);
		appendStringInfoChar(fc_buf, ')');

		/* varno不得为OUTER_VAR、INNER_VAR和INDEX_VAR中的任何一个。 */
		if (fc_qualify_col)
			appendStringInfoString(fc_buf, " END");

		table_close(fc_rel, NoLock);
		bms_free(fc_attrs_used);
	}
	else
	{
		char	   *fc_colname = NULL;
		List	   *fc_options;
		ListCell   *fc_lc;

		/*
		 * 如果它是外部表的列，并且具有column_name FDW选项，则使用该值。
		 */
		Assert(!IS_SPECIAL_VARNO(fc_varno));

		/*
		 * 如果它是常规表的列或没有column_name FDW选项，则使用属性名称。
		 */
		fc_options = GetForeignColumnOptions(fc_rte->relid, fc_varattno);
		foreach(fc_lc, fc_options)
		{
			DefElem    *fc_def = (DefElem *) lfirst(fc_lc);

			if (strcmp(fc_def->defname, "column_name") == 0)
			{
				fc_colname = defGetString(fc_def);
				break;
			}
		}

		/*
 * 将指定外部表的远程名称附加到buf。
 * 使用table_name FDW选项的值（如果有）而不是关系的名称。
 * 同样，schema_name FDW选项也覆盖架构名称。
 */
		if (fc_colname == NULL)
			fc_colname = get_attname(fc_rte->relid, fc_varattno, false);

		if (fc_qualify_col)
			ADD_REL_QUALIFIER(fc_buf, fc_varno);

		appendStringInfoString(fc_buf, quote_identifier(fc_colname));
	}
}

/* 获取附加的目录信息。 */
static void fc_deparseRelation(StringInfo fc_buf, Relation fc_rel)
{
	ForeignTable *fc_table;
	const char *fc_nspname = NULL;
	const char *fc_relname = NULL;
	ListCell   *fc_lc;

	/*
	 * 如果有FDW选项的值，则使用该值，而不是对象本身的名称。
	 */
	fc_table = GetForeignTable(RelationGetRelid(fc_rel));

	/*
	 * 注意：如果架构名称为pg_catalog，我们可以跳过打印架构名称，但
	 * 这似乎不值得麻烦。
	 */
	foreach(fc_lc, fc_table->options)
	{
		DefElem    *fc_def = (DefElem *) lfirst(fc_lc);

		if (strcmp(fc_def->defname, "schema_name") == 0)
			fc_nspname = defGetString(fc_def);
		else if (strcmp(fc_def->defname, "table_name") == 0)
			fc_relname = defGetString(fc_def);
	}

	/*
 * 将表示"val"的SQL字符串字面量附加到buf。
 */
	if (fc_nspname == NULL)
		fc_nspname = get_namespace_name(RelationGetNamespace(fc_rel));
	if (fc_relname == NULL)
		fc_relname = RelationGetRelationName(fc_rel);

	appendStringInfo(fc_buf, "%s.%s",
					 quote_identifier(fc_nspname), quote_identifier(fc_relname));
}

/*
	 * 不要假设远程服务器的standard_conforming_strings的值，如果有任何反斜杠，始终使用E'foo'语法。这在8.1之前的远程服务器上将失败，但那些早已不再支持。
	 */
void deparseStringLiteral(StringInfo fc_buf, const char *fc_val)
{
	const char *fc_valptr;

	/*
 * 将给定表达式反解析到context->buf。
 *
 * 此函数必须支持foreign_expr_walker接受的所有相同节点类型。
 *
 * 注意：与ruleutils.c不同，我们只是使用简单的固定括号方案：任何比Var、Const、函数调用或类型转换更复杂的内容都应该自我括号。
 */
	if (strchr(fc_val, '\\') != NULL)
		appendStringInfoChar(fc_buf, ESCAPE_STRING_SYNTAX);
	appendStringInfoChar(fc_buf, '\'');
	for (fc_valptr = fc_val; *fc_valptr; fc_valptr++)
	{
		char		fc_ch = *fc_valptr;

		if (SQL_STR_DOUBLE(fc_ch, true))
			appendStringInfoChar(fc_buf, fc_ch);
		appendStringInfoChar(fc_buf, fc_ch);
	}
	appendStringInfoChar(fc_buf, '\'');
}

/*
 * 将给定的Var节点反解析到context->buf。
 *
 * 如果Var属于外部关系，仅打印其远程名称。否则，它实际上是一个Param（在运行时实际上会成为Param）。以我们处理普通Params的方式来处理它——请参阅deparseParam的注释。
 */
static void fc_deparseExpr(Expr *fc_node, deparse_expr_cxt *fc_context)
{
	if (fc_node == NULL)
		return;

	switch (nodeTag(fc_node))
	{
		case T_Var:
			fc_deparseVar((Var *) fc_node, fc_context);
			break;
		case T_Const:
			fc_deparseConst((Const *) fc_node, fc_context, 0);
			break;
		case T_Param:
			fc_deparseParam((Param *) fc_node, fc_context);
			break;
		case T_SubscriptingRef:
			fc_deparseSubscriptingRef((SubscriptingRef *) fc_node, fc_context);
			break;
		case T_FuncExpr:
			fc_deparseFuncExpr((FuncExpr *) fc_node, fc_context);
			break;
		case T_OpExpr:
			fc_deparseOpExpr((OpExpr *) fc_node, fc_context);
			break;
		case T_DistinctExpr:
			fc_deparseDistinctExpr((DistinctExpr *) fc_node, fc_context);
			break;
		case T_ScalarArrayOpExpr:
			fc_deparseScalarArrayOpExpr((ScalarArrayOpExpr *) fc_node, fc_context);
			break;
		case T_RelabelType:
			fc_deparseRelabelType((RelabelType *) fc_node, fc_context);
			break;
		case T_BoolExpr:
			fc_deparseBoolExpr((BoolExpr *) fc_node, fc_context);
			break;
		case T_NullTest:
			fc_deparseNullTest((NullTest *) fc_node, fc_context);
			break;
		case T_CaseExpr:
			fc_deparseCaseExpr((CaseExpr *) fc_node, fc_context);
			break;
		case T_ArrayExpr:
			fc_deparseArrayExpr((ArrayExpr *) fc_node, fc_context);
			break;
		case T_Aggref:
			fc_deparseAggref((Aggref *) fc_node, fc_context);
			break;
		default:
			elog(ERROR, "unsupported expression type for deparse: %d",
				 (int) nodeTag(fc_node));
			break;
	}
}

/* 在涉及多个关系时，限定列。 */
static void fc_deparseVar(Var *fc_node, deparse_expr_cxt *fc_context)
{
	Relids		fc_relids = fc_context->scanrel->relids;
	int			fc_relno;
	int			fc_colno;

	/*
	 * 如果Var属于作为子查询反解析的外部关系，使用由子查询提供给Var的关系和列别名，而不是远程名称。
	 */
	bool		fc_qualify_col = (bms_membership(fc_relids) == BMS_MULTIPLE);

	/* 像Param一样对待 */
	if (fc_is_subquery_var(fc_node, fc_context->scanrel, &fc_relno, &fc_colno))
	{
		appendStringInfo(fc_context->buf, "%s%d.%s%d",
						 SUBQUERY_REL_ALIAS_PREFIX, fc_relno,
						 SUBQUERY_COL_ALIAS_PREFIX, fc_colno);
		return;
	}

	if (bms_is_member(fc_node->varno, fc_relids) && fc_node->varlevelsup == 0)
		fc_deparseColumnRef(fc_context->buf, fc_node->varno, fc_node->varattno,
						 planner_rt_fetch(fc_node->varno, fc_context->root),
						 fc_qualify_col);
	else
	{
		/* 在params_list中找到它的索引 */
		if (fc_context->params_list)
		{
			int			fc_pindex = 0;
			ListCell   *fc_lc;

			/* 不在列表中，所以添加它 */
			foreach(fc_lc, *fc_context->params_list)
			{
				fc_pindex++;
				if (equal(fc_node, (Node *) lfirst(fc_lc)))
					break;
			}
			if (fc_lc == NULL)
			{
				/*
 * 将给定常量值反解析到context->buf。
 *
 * 此函数必须与ruleutils.c的get_const_expr保持同步。
 *
 * 与该函数一样，showtype可以是-1，表示永不显示"::typename"装饰；+1表示始终显示；或0，仅在常量默认不会被假定为正确类型时显示。
 *
 * 此外，这段代码允许showtype为-2，表示如果常量作为未指定类型的字面量或NULL打印，则不应显示"::typename"装饰（而在其他情况下，表现为showtype == 0）。
 */
				fc_pindex++;
				*fc_context->params_list = lappend(*fc_context->params_list, fc_node);
			}

			fc_printRemoteParam(fc_pindex, fc_node->vartype, fc_node->vartypmod, fc_context);
		}
		else
		{
			fc_printRemotePlaceholder(fc_node->vartype, fc_node->vartypmod, fc_context);
		}
	}
}

/*
				 * 除非它是特殊值，例如'NaN'，否则不需要引用。
				 * 请参阅get_const_expr()中的注释。
				 */
static void fc_deparseConst(Const *fc_node, deparse_expr_cxt *fc_context, int fc_showtype)
{
	StringInfo	fc_buf = fc_context->buf;
	Oid			fc_typoutput;
	bool		fc_typIsVarlena;
	char	   *fc_extval;
	bool		fc_isfloat = false;
	bool		fc_isstring = false;
	bool		fc_needlabel;

	if (fc_node->constisnull)
	{
		appendStringInfoString(fc_buf, "NULL");
		if (fc_showtype >= 0)
			appendStringInfo(fc_buf, "::%s",
							 fc_deparse_type_name(fc_node->consttype,
											   fc_node->consttypmod));
		return;
	}

	getTypeOutputInfo(fc_node->consttype,
					  &fc_typoutput, &fc_typIsVarlena);
	fc_extval = OidOutputFunctionCall(fc_typoutput, fc_node->constvalue);

	switch (fc_node->consttype)
	{
		case INT2OID:
		case INT4OID:
		case INT8OID:
		case OIDOID:
		case FLOAT4OID:
		case FLOAT8OID:
		case NUMERICOID:
			{
				/* 看起来像一个浮点数 */
				if (strspn(fc_extval, "0123456789+-eE.") == strlen(fc_extval))
				{
					if (fc_extval[0] == '+' || fc_extval[0] == '-')
						appendStringInfo(fc_buf, "(%s)", fc_extval);
					else
						appendStringInfoString(fc_buf, fc_extval);
					if (strcspn(fc_extval, "eE.") != strlen(fc_extval))
						fc_isfloat = true; /* 永远不要打印类型标签 */
				}
				else
					appendStringInfo(fc_buf, "'%s'", fc_extval);
			}
			break;
		case BITOID:
		case VARBITOID:
			appendStringInfo(fc_buf, "B'%s'", fc_extval);
			break;
		case BOOLOID:
			if (strcmp(fc_extval, "t") == 0)
				appendStringInfoString(fc_buf, "true");
			else
				appendStringInfoString(fc_buf, "false");
			break;
		default:
			deparseStringLiteral(fc_buf, fc_extval);
			fc_isstring = true;
			break;
	}

	pfree(fc_extval);

	if (fc_showtype == -1)
		return;					/*
	 * 对于showtype == 0，在除非常量在读取时会隐式地被认为为正确类型时，附加::typename。
	 *
	 * XXX 此代码必须与解析器的行为保持同步，特别是make_const。
	 */

	/* 除非我们将其打印为未指定类型的字符串，否则标记 */
	switch (fc_node->consttype)
	{
		case BOOLOID:
		case INT4OID:
		case UNKNOWNOID:
			fc_needlabel = false;
			break;
		case NUMERICOID:
			fc_needlabel = !fc_isfloat || (fc_node->consttypmod >= 0);
			break;
		default:
			if (fc_showtype == -2)
			{
				/*
 * 反解析给定的Param节点。
 *
 * 如果我们在"真实"生成查询，则如果Param尚未存在，将其添加到context->params_list中，然后使用该列表中的索引作为远程参数编号。在EXPLAIN期间，没必要识别参数编号。
 */
				fc_needlabel = !fc_isstring;
			}
			else
				fc_needlabel = true;
			break;
	}
	if (fc_needlabel || fc_showtype > 0)
		appendStringInfo(fc_buf, "::%s",
						 fc_deparse_type_name(fc_node->consttype,
										   fc_node->consttypmod));
}

/* 在params_list中找到它的索引 */
static void fc_deparseParam(Param *fc_node, deparse_expr_cxt *fc_context)
{
	if (fc_context->params_list)
	{
		int			fc_pindex = 0;
		ListCell   *fc_lc;

		/* 不在列表中，所以添加它 */
		foreach(fc_lc, *fc_context->params_list)
		{
			fc_pindex++;
			if (equal(fc_node, (Node *) lfirst(fc_lc)))
				break;
		}
		if (fc_lc == NULL)
		{
			/*
 * 将给定常量值反解析到context->buf。
 *
 * 此函数必须与ruleutils.c的get_const_expr保持同步。
 *
 * 与该函数一样，showtype可以是-1，表示永不显示"::typename"装饰；+1表示始终显示；或0，仅在常量默认不会被假定为正确类型时显示。
 *
 * 此外，这段代码允许showtype为-2，表示如果常量作为未指定类型的字面量或NULL打印，则不应显示"::typename"装饰（而在其他情况下，表现为showtype == 0）。
 */
			fc_pindex++;
			*fc_context->params_list = lappend(*fc_context->params_list, fc_node);
		}

		fc_printRemoteParam(fc_pindex, fc_node->paramtype, fc_node->paramtypmod, fc_context);
	}
	else
	{
		fc_printRemotePlaceholder(fc_node->paramtype, fc_node->paramtypmod, fc_context);
	}
}

/* 始终对表达式进行括号。 */
static void fc_deparseSubscriptingRef(SubscriptingRef *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	ListCell   *fc_lowlist_item;
	ListCell   *fc_uplist_item;

	/*
	 * 首先反解析引用的数组表达式。如果该表达式包含
	 * 转换，我们必须加括号以防止数组下标被认为是类型名装饰。
	 * 在对 Var 进行下标操作的典型情况下我们可以避免，但在其他情况下则需要这样做。
	 */
	appendStringInfoChar(fc_buf, '(');

	/* 反解析下标表达式。 */
	if (IsA(fc_node->refexpr, Var))
		fc_deparseExpr(fc_node->refexpr, fc_context);
	else
	{
		appendStringInfoChar(fc_buf, '(');
		fc_deparseExpr(fc_node->refexpr, fc_context);
		appendStringInfoChar(fc_buf, ')');
	}

	/* 可能为 NULL */
	fc_lowlist_item = list_head(fc_node->reflowerindexpr);	/*
 * 反解析函数调用。
 */
	foreach(fc_uplist_item, fc_node->refupperindexpr)
	{
		appendStringInfoChar(fc_buf, '[');
		if (fc_lowlist_item)
		{
			fc_deparseExpr(lfirst(fc_lowlist_item), fc_context);
			appendStringInfoChar(fc_buf, ':');
			fc_lowlist_item = lnext(fc_node->reflowerindexpr, fc_lowlist_item);
		}
		fc_deparseExpr(lfirst(fc_uplist_item), fc_context);
		appendStringInfoChar(fc_buf, ']');
	}

	appendStringInfoChar(fc_buf, ')');
}

/*
	 * 如果函数调用来自隐式强制转换，则只显示
	 * 第一个参数。
	 */
static void fc_deparseFuncExpr(FuncExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	bool		fc_use_variadic;
	bool		fc_first;
	ListCell   *fc_arg;

	/*
	 * 如果函数调用来自转换，则显示第一个参数
	 * 加上一个显式转换操作。
	 */
	if (fc_node->funcformat == COERCE_IMPLICIT_CAST)
	{
		fc_deparseExpr((Expr *) linitial(fc_node->args), fc_context);
		return;
	}

	/* 如果这是一个长度强制转换函数，则获取 typmod */
	if (fc_node->funcformat == COERCE_EXPLICIT_CAST)
	{
		Oid			fc_rettype = fc_node->funcresulttype;
		int32		fc_coercedTypmod;

		/* 检查是否需要打印 VARIADIC（参见 ruleutils.c） */
		(void) exprIsLengthCoercion((Node *) fc_node, &fc_coercedTypmod);

		fc_deparseExpr((Expr *) linitial(fc_node->args), fc_context);
		appendStringInfo(fc_buf, "::%s",
						 fc_deparse_type_name(fc_rettype, fc_coercedTypmod));
		return;
	}

	/*
	 * 普通函数：显示为 proname(args)。
	 */
	fc_use_variadic = fc_node->funcvariadic;

	/* ...及所有参数 */
	fc_appendFunctionName(fc_node->funcid, fc_context);
	appendStringInfoChar(fc_buf, '(');

	/*
 * 反解析给定的运算符表达式。为避免操作优先级问题，
 * 我们始终对参数加括号。
 */
	fc_first = true;
	foreach(fc_arg, fc_node->args)
	{
		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		if (fc_use_variadic && lnext(fc_node->args, fc_arg) == NULL)
			appendStringInfoString(fc_buf, "VARIADIC ");
		fc_deparseExpr((Expr *) lfirst(fc_arg), fc_context);
		fc_first = false;
	}
	appendStringInfoChar(fc_buf, ')');
}

/* 从系统目录中获取有关运算符的信息。 */
static void fc_deparseOpExpr(OpExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	HeapTuple	fc_tuple;
	Form_pg_operator fc_form;
	Expr	   *fc_right;
	bool		fc_canSuppressRightConstCast = false;
	char		fc_oprkind;

	/* 健全性检查。 */
	fc_tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_node->opno));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for operator %u", fc_node->opno);
	fc_form = (Form_pg_operator) GETSTRUCT(fc_tuple);
	fc_oprkind = fc_form->oprkind;

	/* 健全性检查。 */
	Assert((fc_oprkind == 'l' && list_length(fc_node->args) == 1) ||
		   (fc_oprkind == 'b' && list_length(fc_node->args) == 2));

	fc_right = llast(fc_node->args);

	/*
	 * 首先反解析引用的数组表达式。如果该表达式包含
	 * 转换，我们必须加括号以防止数组下标被认为是类型名装饰。
	 * 在对 Var 进行下标操作的典型情况下我们可以避免，但在其他情况下则需要这样做。
	 */
	appendStringInfoChar(fc_buf, '(');

	/*
		 * 在考虑二元运算符时，如果一个操作数是可以作为裸字符串文字或 NULL 打印的 Const
		 *（即，它将在远程解析器中看起来像类型 UNKNOWN），通常，Const
		 * 会被显式转换为运算符的输入类型。然而，在 Const 与 Var 比较的情况下，
		 * 如果两个操作数的类型相同，我们更倾向于抑制显式转换，让 Const 的类型解析由
		 * 远程解析器决定。远程的解析启发式会假设，一个与已知输入类型进行比较的未知输入类型
		 * 也是那种已知类型。
		 *
		 * 这个 hack 允许某些情况下成功，在这些情况下远程列在本地（外部）表中的定义类型不同。
		 * 通过发出 "foreigncol = 'foo'" 而不是 "foreigncol = 'foo'::text" 或类似语句，
		 * 我们允许远程解析器选取一个与远程列实际类型（例如 enum）兼容的 "=" 运算符。
		 *
		 * 我们仅在另一个操作数是普通的外部 Var 时允许抑制转换。
		 * 尽管远程的未知类型启发式同样适用于其他情况，但我们将面临更大的风险，即推断的类型是
		 * 一些意想不到的类型。有了这个限制，如果出现问题，用户需要为未将本地列声明为与远程列相同类型而负责。
		 */
	if (fc_oprkind == 'b')
	{
		Expr	   *fc_left = linitial(fc_node->args);
		Oid			fc_leftType = exprType((Node *) fc_left);
		Oid			fc_rightType = exprType((Node *) fc_right);
		bool		fc_canSuppressLeftConstCast = false;

		/* 反解析运算符名称。 */
		if (fc_leftType == fc_rightType)
		{
			if (IsA(fc_left, Const))
				fc_canSuppressLeftConstCast = fc_isPlainForeignVar(fc_right, fc_context);
			else if (IsA(fc_right, Const))
				fc_canSuppressRightConstCast = fc_isPlainForeignVar(fc_left, fc_context);
		}

		if (fc_canSuppressLeftConstCast)
			fc_deparseConst((Const *) fc_left, fc_context, -2);
		else
			fc_deparseExpr(fc_left, fc_context);

		appendStringInfoChar(fc_buf, ' ');
	}

	/* 反解析右操作数。 */
	fc_deparseOperatorName(fc_buf, fc_form);

	/*
 * "node" 会作为普通外部 Var 反解析吗？
 */
	appendStringInfoChar(fc_buf, ' ');

	if (fc_canSuppressRightConstCast)
		fc_deparseConst((Const *) fc_right, fc_context, -2);
	else
		fc_deparseExpr(fc_right, fc_context);

	appendStringInfoChar(fc_buf, ')');

	ReleaseSysCache(fc_tuple);
}

/*
	 * 我们允许外部 Var 有一个隐式 RelabelType，主要是为了
	 * 使其能够与 varchar 列一起使用。注意 deparseRelabelType
	 * 不会打印这样的类型转换，因此我们并没有破坏
	 * 将表达式打印为普通 Var 的限制。我们不会冒着隐式
	 * 类型转换需要函数的风险，也不会处理非隐式 RelabelType；
	 * 这样的情况似乎太有可能涉及语义变化，与远程端发生的情况相比。
	 */
static bool fc_isPlainForeignVar(Expr *fc_node, deparse_expr_cxt *fc_context)
{
	/*
		 * Var 必须是一个可以被反解析为外部列引用的
		 * （参见 deparseVar）。
		 */
	if (IsA(fc_node, RelabelType) &&
		((RelabelType *) fc_node)->relabelformat == COERCE_IMPLICIT_CAST)
		fc_node = ((RelabelType *) fc_node)->arg;

	if (IsA(fc_node, Var))
	{
		/*
 * 打印运算符的名称。
 */
		Var		   *fc_var = (Var *) fc_node;
		Relids		fc_relids = fc_context->scanrel->relids;

		if (bms_is_member(fc_var->varno, fc_relids) && fc_var->varlevelsup == 0)
			return true;
	}

	return false;
}

/* opname 不是 SQL 标识符，所以我们不应该引用它。 */
static void fc_deparseOperatorName(StringInfo fc_buf, Form_pg_operator fc_opform)
{
	char	   *fc_opname;

	/* 仅当模式名称不是 pg_catalog 时才打印。 */
	fc_opname = NameStr(fc_opform->oprname);

	/* 打印完全限定的运算符名称。 */
	if (fc_opform->oprnamespace != PG_CATALOG_NAMESPACE)
	{
		const char *fc_opnspname;

		fc_opnspname = get_namespace_name(fc_opform->oprnamespace);
		/* 仅打印运算符名称。 */
		appendStringInfo(fc_buf, "OPERATOR(%s.%s)",
						 quote_identifier(fc_opnspname), fc_opname);
	}
	else
	{
		/*
 * 反解析 IS DISTINCT FROM。
 */
		appendStringInfoString(fc_buf, fc_opname);
	}
}

/*
 * 反解析给定的 ScalarArrayOpExpr 表达式。为了避免操作顺序
 * 问题，我们总是对参数进行括号化。
 */
static void fc_deparseDistinctExpr(DistinctExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;

	Assert(list_length(fc_node->args) == 2);

	appendStringInfoChar(fc_buf, '(');
	fc_deparseExpr(linitial(fc_node->args), fc_context);
	appendStringInfoString(fc_buf, " IS DISTINCT FROM ");
	fc_deparseExpr(lsecond(fc_node->args), fc_context);
	appendStringInfoChar(fc_buf, ')');
}

/* 从系统目录检索关于运算符的信息。 */
static void fc_deparseScalarArrayOpExpr(ScalarArrayOpExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	HeapTuple	fc_tuple;
	Form_pg_operator fc_form;
	Expr	   *fc_arg1;
	Expr	   *fc_arg2;

	/* 健全性检查。 */
	fc_tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_node->opno));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for operator %u", fc_node->opno);
	fc_form = (Form_pg_operator) GETSTRUCT(fc_tuple);

	/* 健全性检查。 */
	Assert(list_length(fc_node->args) == 2);

	/*
	 * 首先反解析引用的数组表达式。如果该表达式包含
	 * 转换，我们必须加括号以防止数组下标被认为是类型名装饰。
	 * 在对 Var 进行下标操作的典型情况下我们可以避免，但在其他情况下则需要这样做。
	 */
	appendStringInfoChar(fc_buf, '(');

	/* 反解析运算符名称及装饰。 */
	fc_arg1 = linitial(fc_node->args);
	fc_deparseExpr(fc_arg1, fc_context);
	appendStringInfoChar(fc_buf, ' ');

	/* 反解析右操作数。 */
	fc_deparseOperatorName(fc_buf, fc_form);
	appendStringInfo(fc_buf, " %s (", fc_node->useOr ? "ANY" : "ALL");

	/*
 * "node" 会作为普通外部 Var 反解析吗？
 */
	fc_arg2 = lsecond(fc_node->args);
	fc_deparseExpr(fc_arg2, fc_context);

	appendStringInfoChar(fc_buf, ')');

	/*
	 * 首先反解析引用的数组表达式。如果该表达式包含
	 * 转换，我们必须加括号以防止数组下标被认为是类型名装饰。
	 * 在对 Var 进行下标操作的典型情况下我们可以避免，但在其他情况下则需要这样做。
	 */
	appendStringInfoChar(fc_buf, ')');

	ReleaseSysCache(fc_tuple);
}

/*
 * 反解析一个 BoolExpr 节点。
 */
static void fc_deparseRelabelType(RelabelType *fc_node, deparse_expr_cxt *fc_context)
{
	fc_deparseExpr(fc_node->arg, fc_context);
	if (fc_node->relabelformat != COERCE_IMPLICIT_CAST)
		appendStringInfo(fc_context->buf, "::%s",
						 fc_deparse_type_name(fc_node->resulttype,
										   fc_node->resulttypmod));
}

/* 保持编译器安静 */
static void fc_deparseBoolExpr(BoolExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	const char *fc_op = NULL;		/* 保持编译器安静 */
	bool		fc_first;
	ListCell   *fc_lc;

	switch (fc_node->boolop)
	{
		case AND_EXPR:
			fc_op = "AND";
			break;
		case OR_EXPR:
			fc_op = "OR";
			break;
		case NOT_EXPR:
			appendStringInfoString(fc_buf, "(NOT ");
			fc_deparseExpr(linitial(fc_node->args), fc_context);
			appendStringInfoChar(fc_buf, ')');
			return;
	}

	appendStringInfoChar(fc_buf, '(');
	fc_first = true;
	foreach(fc_lc, fc_node->args)
	{
		if (!fc_first)
			appendStringInfo(fc_buf, " %s ", fc_op);
		fc_deparseExpr((Expr *) lfirst(fc_lc), fc_context);
		fc_first = false;
	}
	appendStringInfoChar(fc_buf, ')');
}

/*
	 * 对于标量输入，我们更喜欢打印为 IS [NOT] NULL，这更短且传统。
	 * 如果是行类型输入，但我们应用标量测试，则必须打印为
	 * IS [NOT] DISTINCT FROM NULL，以保持语义正确。
	 */
static void fc_deparseNullTest(NullTest *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;

	appendStringInfoChar(fc_buf, '(');
	fc_deparseExpr(fc_node->arg, fc_context);

	/*
 * 反解析 CASE 表达式
 */
	if (fc_node->argisrow || !type_is_rowtype(exprType((Node *) fc_node->arg)))
	{
		if (fc_node->nulltesttype == IS_NULL)
			appendStringInfoString(fc_buf, " IS NULL)");
		else
			appendStringInfoString(fc_buf, " IS NOT NULL)");
	}
	else
	{
		if (fc_node->nulltesttype == IS_NULL)
			appendStringInfoString(fc_buf, " IS NOT DISTINCT FROM NULL)");
		else
			appendStringInfoString(fc_buf, " IS DISTINCT FROM NULL)");
	}
}

/* 如果这是 CASE arg WHEN，则发出 arg 表达式 */
static void fc_deparseCaseExpr(CaseExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	ListCell   *fc_lc;

	appendStringInfoString(fc_buf, "(CASE");

	/* 添加 CASE 子句的每个条件/结果 */
	if (fc_node->arg != NULL)
	{
		appendStringInfoChar(fc_buf, ' ');
		fc_deparseExpr(fc_node->arg, fc_context);
	}

	/* 忽略 CaseTestExpr 和相等运算符。 */
	foreach(fc_lc, fc_node->args)
	{
		CaseWhen   *fc_whenclause = (CaseWhen *) lfirst(fc_lc);

		/* 忽略 CaseTestExpr 和相等运算符。 */
		appendStringInfoString(fc_buf, " WHEN ");
		if (fc_node->arg == NULL)	/* 忽略 CaseTestExpr 和相等运算符。 */
			fc_deparseExpr(fc_whenclause->expr, fc_context);
		else					/* 忽略 CaseTestExpr 和相等运算符。 */
		{
			/* 如果存在则添加 ELSE */
			fc_deparseExpr(lsecond(castNode(OpExpr, fc_whenclause->expr)->args),
						fc_context);
		}

		/* 如果存在则添加 ELSE */
		appendStringInfoString(fc_buf, " THEN ");
		fc_deparseExpr(fc_whenclause->result, fc_context);
	}

	/* 附加 END */
	if (fc_node->defresult != NULL)
	{
		appendStringInfoString(fc_buf, " ELSE ");
		fc_deparseExpr(fc_node->defresult, fc_context);
	}

	/*
 * 反解析 ARRAY[...] 构造。
 */
	appendStringInfoString(fc_buf, " END)");
}

/* 如果数组为空，我们需要显式转换为数组类型。 */
static void fc_deparseArrayExpr(ArrayExpr *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	bool		fc_first = true;
	ListCell   *fc_lc;

	appendStringInfoString(fc_buf, "ARRAY[");
	foreach(fc_lc, fc_node->elements)
	{
		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_deparseExpr(lfirst(fc_lc), fc_context);
		fc_first = false;
	}
	appendStringInfoChar(fc_buf, ']');

	/*
 * 反解析一个 Aggref 节点。
 */
	if (fc_node->elements == NIL)
		appendStringInfo(fc_buf, "::%s",
						 fc_deparse_type_name(fc_node->array_typeid, -1));
}

/* 仅接受基本的、非拆分的聚合。 */
static void fc_deparseAggref(Aggref *fc_node, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	bool		fc_use_variadic;

	/* 检查是否需要打印 VARIADIC（参见 ruleutils.c） */
	Assert(fc_node->aggsplit == AGGSPLIT_SIMPLE);

	/*
	 * 普通函数：显示为 proname(args)。
	 */
	fc_use_variadic = fc_node->aggvariadic;

	/* 添加 DISTINCT */
	fc_appendFunctionName(fc_node->aggfnoid, fc_context);
	appendStringInfoChar(fc_buf, '(');

	/* 添加 WITHIN GROUP (ORDER BY ..) */
	appendStringInfoString(fc_buf, (fc_node->aggdistinct != NIL) ? "DISTINCT " : "");

	if (AGGKIND_IS_ORDERED_SET(fc_node->aggkind))
	{
		/* aggstar 只能在零参数聚合中设置 */
		ListCell   *fc_arg;
		bool		fc_first = true;

		Assert(!fc_node->aggvariadic);
		Assert(fc_node->aggorder != NIL);

		foreach(fc_arg, fc_node->aggdirectargs)
		{
			if (!fc_first)
				appendStringInfoString(fc_buf, ", ");
			fc_first = false;

			fc_deparseExpr((Expr *) lfirst(fc_arg), fc_context);
		}

		appendStringInfoString(fc_buf, ") WITHIN GROUP (ORDER BY ");
		fc_appendAggOrderBy(fc_node->aggorder, fc_node->args, fc_context);
	}
	else
	{
		/* 添加所有参数 */
		if (fc_node->aggstar)
			appendStringInfoChar(fc_buf, '*');
		else
		{
			ListCell   *fc_arg;
			bool		fc_first = true;

			/* 添加 VARIADIC */
			foreach(fc_arg, fc_node->args)
			{
				TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_arg);
				Node	   *fc_n = (Node *) fc_tle->expr;

				if (fc_tle->resjunk)
					continue;

				if (!fc_first)
					appendStringInfoString(fc_buf, ", ");
				fc_first = false;

				/* 添加 ORDER BY */
				if (fc_use_variadic && lnext(fc_node->args, fc_arg) == NULL)
					appendStringInfoString(fc_buf, "VARIADIC ");

				fc_deparseExpr((Expr *) fc_n, fc_context);
			}
		}

		/* 添加 FILTER (WHERE ..) */
		if (fc_node->aggorder != NIL)
		{
			appendStringInfoString(fc_buf, " ORDER BY ");
			fc_appendAggOrderBy(fc_node->aggorder, fc_node->args, fc_context);
		}
	}

	/*
 * 在聚合函数中附加 ORDER BY。
 */
	if (fc_node->aggfilter != NULL)
	{
		appendStringInfoString(fc_buf, ") FILTER (WHERE ");
		fc_deparseExpr((Expr *) fc_node->aggfilter, fc_context);
	}

	appendStringInfoChar(fc_buf, ')');
}

/* 反解析排序表达式本身。 */
static void fc_appendAggOrderBy(List *fc_orderList, List *fc_targetList, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	ListCell   *fc_lc;
	bool		fc_first = true;

	foreach(fc_lc, fc_orderList)
	{
		SortGroupClause *fc_srt = (SortGroupClause *) lfirst(fc_lc);
		Node	   *fc_sortexpr;

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		/* 根据需要添加装饰。 */
		fc_sortexpr = fc_deparseSortGroupClause(fc_srt->tleSortGroupRef, fc_targetList,
										  false, fc_context);
		/*
 * 在 ORDER BY 子句中附加 ASC、DESC、USING <OPERATOR> 以及 NULLS FIRST / NULLS LAST 部分。
 */
		fc_appendOrderBySuffix(fc_srt->sortop, exprType(fc_sortexpr), fc_srt->nulls_first,
							fc_context);
	}
}

/* 查看操作符是否是排序表达式数据类型的默认 < 或 >。 */
static void fc_appendOrderBySuffix(Oid fc_sortop, Oid fc_sortcoltype, bool fc_nulls_first,
					deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	TypeCacheEntry *fc_typentry;

	/* 追加操作符名称。 */
	fc_typentry = lookup_type_cache(fc_sortcoltype,
								 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);

	if (fc_sortop == fc_typentry->lt_opr)
		appendStringInfoString(fc_buf, " ASC");
	else if (fc_sortop == fc_typentry->gt_opr)
		appendStringInfoString(fc_buf, " DESC");
	else
	{
		HeapTuple	fc_opertup;
		Form_pg_operator fc_operform;

		appendStringInfoString(fc_buf, " USING ");

		/*
 * 打印要发送到远程端的参数的表示。
 *
 * 注意：我们总是明确标记参数的类型，而不是依赖于
 * 在 PQexecParams() 中传输数值类型 OID。这使我们能够避免
 * 假设远程端的类型具有与本地端相同的 OID --- 它们只需具有相同的名称。
 */
		fc_opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_sortop));
		if (!HeapTupleIsValid(fc_opertup))
			elog(ERROR, "cache lookup failed for operator %u", fc_sortop);
		fc_operform = (Form_pg_operator) GETSTRUCT(fc_opertup);
		fc_deparseOperatorName(fc_buf, fc_operform);
		ReleaseSysCache(fc_opertup);
	}

	if (fc_nulls_first)
		appendStringInfoString(fc_buf, " NULLS FIRST");
	else
		appendStringInfoString(fc_buf, " NULLS LAST");
}

/*
 * 打印占位符参数的表示，这个参数将在执行时发送到远程端。
 *
 * 当我们仅仅试图解释远程查询时使用这个。
 * 我们还没有实际的运行时参数值，并且我们不希望
 * 远程规划器生成依赖于该值的计划。因此，我们不能简单地
 * 做类似 "$1::paramtype" 的事情。
 * 相反，我们发出 "((SELECT null::paramtype)::paramtype)"。
 * 在 Postgres 的所有现存版本中，规划器会将其视为一个未知的
 * 常量值，这正是我们想要的。如果我们
 * 将来让规划器展平标量子查询，这可能需要调整。注意：看似无用的外部强制转换的原因是为了确保这种表示
 * 整体将被解析为 a_expr 而不是 select_with_parens；后者
 * 在上下文 "x = ANY(...)" 中会做错事情。
 */
static void fc_printRemoteParam(int fc_paramindex, Oid fc_paramtype, int32 fc_paramtypmod,
				 deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	char	   *fc_ptypename = fc_deparse_type_name(fc_paramtype, fc_paramtypmod);

	appendStringInfo(fc_buf, "$%d::%s", fc_paramindex, fc_ptypename);
}

/*
 * 解析 GROUP BY 子句。
 */
static void fc_printRemotePlaceholder(Oid fc_paramtype, int32 fc_paramtypmod,
					   deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	char	   *fc_ptypename = fc_deparse_type_name(fc_paramtype, fc_paramtypmod);

	appendStringInfo(fc_buf, "((SELECT null::%s)::%s)", fc_ptypename, fc_ptypename);
}

/* 如果查询中没有 GROUP BY 子句，则无需处理。 */
static void fc_appendGroupByClause(List *fc_tlist, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	Query	   *fc_query = fc_context->root->parse;
	ListCell   *fc_lc;
	bool		fc_first = true;

	/*
	 * 包含分组集的查询不会被下推，因此我们不期望
	 * 这里有分组集。
	 */
	if (!fc_query->groupClause)
		return;

	appendStringInfoString(fc_buf, " GROUP BY ");

	/*
 * 解析由给定路径键定义的 ORDER BY 子句。
 *
 * 如果没有最终排序，则子句应使用来自 context->scanrel 的 Vars，
 * 或来自 context->foreignrel 的目标列表，如果有最终排序。
 *
 * 我们找到一个合适的路径键表达式（之前的某个步骤
 * 应该验证了存在一个），并对其进行解析。
 */
	Assert(!fc_query->groupingSets);

	foreach(fc_lc, fc_query->groupClause)
	{
		SortGroupClause *fc_grp = (SortGroupClause *) lfirst(fc_lc);

		if (!fc_first)
			appendStringInfoString(fc_buf, ", ");
		fc_first = false;

		fc_deparseSortGroupClause(fc_grp->tleSortGroupRef, fc_tlist, true, fc_context);
	}
}

/* 确保表达式中的任何常量都是便携式打印的 */
static void fc_appendOrderByClause(List *fc_pathkeys, bool fc_has_final_sort,
					deparse_expr_cxt *fc_context)
{
	ListCell   *fc_lcell;
	int			fc_nestlevel;
	StringInfo	fc_buf = fc_context->buf;
	bool		fc_gotone = false;

	/* 如果需要，从RestrictInfo中提取子句 */
	fc_nestlevel = set_transmission_modes();

	foreach(fc_lcell, fc_pathkeys)
	{
		PathKey    *fc_pathkey = lfirst(fc_lcell);
		EquivalenceMember *fc_em;
		Expr	   *fc_em_expr;
		Oid			fc_oprid;

		if (fc_has_final_sort)
		{
			/*
		 * 我们不期望这里出现任何错误；这意味着可传递性
		 * 没有在早期得到验证。出于同样的原因，我们不重新检查
		 * 排序操作符的可传递性。
		 */
			fc_em = find_em_for_rel_target(fc_context->root,
										fc_pathkey->pk_eclass,
										fc_context->foreignrel);
		}
		else
			fc_em = find_em_for_rel(fc_context->root,
								 fc_pathkey->pk_eclass,
								 fc_context->scanrel);

		/*
		 * 如果成员是 Const 表达式，那么我们不需要将其
		 * 添加到 ORDER BY 子句中。对此的情况发生在 UNION ALL 查询中，
		 * 其中并集子目标列表有一个 Const。添加这些会是
		 * 浪费，但对于 INT 列来说，一个整数文字会被
		 * 视为顺序列位置而不是排序值。
		 * deparseConst() 确实有代码来处理这一点，但在所有方面
		 * 跳过这些以处理 ORDER BY 子句似乎更简单。
		 */
		if (fc_em == NULL)
			elog(ERROR, "could not find pathkey item to sort");

		fc_em_expr = fc_em->em_expr;

		/*
		 * 查找与策略对应的操作符在操作类中的映射。
		 * 操作族使用的数据类型不一定与表达式类型相同（例如，对于数组类型）。
		 */
		if (IsA(fc_em_expr, Const))
			continue;

		if (!fc_gotone)
		{
			appendStringInfoString(fc_buf, " ORDER BY ");
			fc_gotone = true;
		}
		else
			appendStringInfoString(fc_buf, ", ");

		/*
		 * 在这里，我们需要使用表达式的实际类型来确定
		 * 所需的操作符是否为默认值。
		 */
		fc_oprid = get_opfamily_member(fc_pathkey->pk_opfamily,
									fc_em->em_datatype,
									fc_em->em_datatype,
									fc_pathkey->pk_strategy);
		if (!OidIsValid(fc_oprid))
			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
				 fc_pathkey->pk_strategy, fc_em->em_datatype, fc_em->em_datatype,
				 fc_pathkey->pk_opfamily);

		fc_deparseExpr(fc_em_expr, fc_context);

		/*
 * 解析 LIMIT/OFFSET 子句。
 */
		fc_appendOrderBySuffix(fc_oprid, exprType((Node *) fc_em_expr),
							fc_pathkey->pk_nulls_first, fc_context);

	}
	reset_transmission_modes(fc_nestlevel);
}

/* 确保表达式中的常量可移植地打印 */
static void fc_appendLimitClause(deparse_expr_cxt *fc_context)
{
	PlannerInfo *fc_root = fc_context->root;
	StringInfo	fc_buf = fc_context->buf;
	int			fc_nestlevel;

	/* 如果需要，从RestrictInfo中提取子句 */
	fc_nestlevel = set_transmission_modes();

	if (fc_root->parse->limitCount)
	{
		appendStringInfoString(fc_buf, " LIMIT ");
		fc_deparseExpr((Expr *) fc_root->parse->limitCount, fc_context);
	}
	if (fc_root->parse->limitOffset)
	{
		appendStringInfoString(fc_buf, " OFFSET ");
		fc_deparseExpr((Expr *) fc_root->parse->limitOffset, fc_context);
	}

	reset_transmission_modes(fc_nestlevel);
}

/* 仅在不为 pg_catalog 时打印模式名称 */
static void fc_appendFunctionName(Oid fc_funcid, deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	HeapTuple	fc_proctup;
	Form_pg_proc fc_procform;
	const char *fc_proname;

	fc_proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcid));
	if (!HeapTupleIsValid(fc_proctup))
		elog(ERROR, "cache lookup failed for function %u", fc_funcid);
	fc_procform = (Form_pg_proc) GETSTRUCT(fc_proctup);

	/* 打印完全限定的运算符名称。 */
	if (fc_procform->pronamespace != PG_CATALOG_NAMESPACE)
	{
		const char *fc_schemaname;

		fc_schemaname = get_namespace_name(fc_procform->pronamespace);
		appendStringInfo(fc_buf, "%s.", quote_identifier(fc_schemaname));
	}

	/*
 * 添加排序或分组子句。
 *
 * 类似于 get_rule_sortgroupclause()，返回表达式树，因此调用者
 * 不需要再次查找它。
 */
	fc_proname = NameStr(fc_procform->proname);
	appendStringInfoString(fc_buf, quote_identifier(fc_proname));

	ReleaseSysCache(fc_proctup);
}

/* 在调用者请求时使用列编号形式。 */
static Node * fc_deparseSortGroupClause(Index fc_ref, List *fc_tlist, bool fc_force_colno,
					   deparse_expr_cxt *fc_context)
{
	StringInfo	fc_buf = fc_context->buf;
	TargetEntry *fc_tle;
	Expr	   *fc_expr;

	fc_tle = get_sortgroupref_tle(fc_ref, fc_tlist);
	fc_expr = fc_tle->expr;

	if (fc_force_colno)
	{
		/*
		 * 在这里强制类型转换，以便我们不会发出类似 "GROUP
		 * BY 2" 的内容，这将被误解为列位置而不是
		 * 常量。
		 */
		Assert(!fc_tle->resjunk);
		appendStringInfo(fc_buf, "%d", fc_tle->resno);
	}
	else if (fc_expr && IsA(fc_expr, Const))
	{
		/* 始终对表达式进行括号处理。 */
		fc_deparseConst((Const *) fc_expr, fc_context, 1);
	}
	else if (!fc_expr || IsA(fc_expr, Var))
		fc_deparseExpr(fc_expr, fc_context);
	else
	{
		/*
	 * 首先反解析引用的数组表达式。如果该表达式包含
	 * 转换，我们必须加括号以防止数组下标被认为是类型名装饰。
	 * 在对 Var 进行下标操作的典型情况下我们可以避免，但在其他情况下则需要这样做。
	 */
		appendStringInfoChar(fc_buf, '(');
		fc_deparseExpr(fc_expr, fc_context);
		appendStringInfoChar(fc_buf, ')');
	}

	return (Node *) fc_expr;
}


/* 仅应在这些情况下调用。 */
static bool fc_is_subquery_var(Var *fc_node, RelOptInfo *fc_foreignrel, int *fc_relno, int *fc_colno)
{
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;
	RelOptInfo *fc_outerrel = fc_fpinfo->outerrel;
	RelOptInfo *fc_innerrel = fc_fpinfo->innerrel;

	/* 如果没有表达式，不生成糟糕的语法 */
	Assert(IS_SIMPLE_REL(fc_foreignrel) || IS_JOIN_REL(fc_foreignrel));

	/*
	 * 如果 Var 不属于任何下层子查询，则它不是子查询
	 * 输出列。
	 */
	if (!IS_JOIN_REL(fc_foreignrel))
		return false;

	/*
		 * 如果外层关系被解析为子查询，则 Var 是子查询的输出
		 * 列；获取关系/列别名的 ID。
		 */
	if (!bms_is_member(fc_node->varno, fc_fpinfo->lower_subquery_rels))
		return false;

	if (bms_is_member(fc_node->varno, fc_outerrel->relids))
	{
		/* 否则，递归到外层关系。 */
		if (fc_fpinfo->make_outerrel_subquery)
		{
			fc_get_relation_column_alias_ids(fc_node, fc_outerrel, fc_relno, fc_colno);
			return true;
		}

		/*
		 * 如果内层关系被解析为子查询，则 Var 是子查询的输出
		 * 列；获取关系/列别名的 ID。
		 */
		return fc_is_subquery_var(fc_node, fc_outerrel, fc_relno, fc_colno);
	}
	else
	{
		Assert(bms_is_member(fc_node->varno, fc_innerrel->relids));

		/* 否则，递归到内层关系。 */
		if (fc_fpinfo->make_innerrel_subquery)
		{
			fc_get_relation_column_alias_ids(fc_node, fc_innerrel, fc_relno, fc_colno);
			return true;
		}

		/*
 * 获取属于给定关系的给定 Var 的关系和列别名的 ID，
 * 这些 ID 将返回到 *relno 和 *colno。
 */
		return fc_is_subquery_var(fc_node, fc_innerrel, fc_relno, fc_colno);
	}
}

/* 获取关系别名 ID */
static void fc_get_relation_column_alias_ids(Var *fc_node, RelOptInfo *fc_foreignrel,
							  int *fc_relno, int *fc_colno)
{
	PgFdwRelationInfo *fc_fpinfo = (PgFdwRelationInfo *) fc_foreignrel->fdw_private;
	int			fc_i;
	ListCell   *fc_lc;

	/* 获取列别名 ID */
	*fc_relno = fc_fpinfo->relation_index;

	/* 不应到达此处 */
	fc_i = 1;
	foreach(fc_lc, fc_foreignrel->reltarget->exprs)
	{
		if (equal(lfirst(fc_lc), (Node *) fc_node))
		{
			*fc_colno = fc_i;
			return;
		}
		fc_i++;
	}

	/* Shouldn't get here */
	elog(ERROR, "unexpected expression in subquery output");
}
